-
Notifications
You must be signed in to change notification settings - Fork 1
Configuring Local Development
This guide explains how to configure TMI for local development: where the config file lives, how secrets are managed, and how to authenticate against a running local server.
TMI configuration is split into two layers. Bootstrap config — database connection, JWT secret, server port, logging — comes from a config-*.yml file (plus TMI_* environment overrides) and is read once at startup. Operational config — OAuth providers, Timmy AI settings, timeouts, feature flags — is stored in the database, seeded from registry defaults on first run, and changed at runtime via the admin API without restarting the server. See Configuration-Model for the full picture.
# 1. Copy the template
cp config-example.yml config-development.yml
# 2. Open config-development.yml and replace the vault:// placeholders
# with real dev values (see "The dev config file" below)
# 3. Create a local cluster and deploy the dev environment
make dev-cluster-up # local kind cluster + registry (laptop path)
make start-dev
# 4. Verify the server is up
curl http://localhost:8080/make start-dev deploys the TMI server and the component platform (Redis, NATS, the controller, and the workers) into a local Kubernetes cluster, delivering config-development.yml as a ConfigMap, then port-forwards the server to port 8080. The database is external — the server connects to whatever database.url your config specifies. See Local Development Cluster for prerequisites, kind/k3s setup, database options (including DB=oracle), and the optional Tilt fast loop.
Note: There is no
/healthendpoint. Use the root endpoint (/) to confirm the server is running.
| File | Tracked in git? | Purpose |
|---|---|---|
config-example.yml |
Yes | Template; shows all bootstrap keys with vault:// placeholders for secrets |
config-development.yml |
No (gitignored) | Your local working config; contains real secrets |
config-test.yml |
No (gitignored) | Used by the test suite |
config-production.yml |
No (gitignored) | Production deployments |
Never commit config-development.yml. It contains real credentials and is gitignored for that reason.
The config file carries only the bootstrap keys — settings the server must have before it can connect to the database:
-
database.url— PostgreSQL connection string (user, password, host, port, database name). This is where you look up your local dev DB credentials. -
server.port,server.tls_*— HTTP/TLS settings -
auth.jwt.secret— Key used to sign and verify JWTs -
logging.*— Log level and format -
secrets.provider— Where TMI should resolvevault://references at runtime
Everything operational (OAuth providers, Timmy, timeouts) is not present here — it lives in the database.
The vault:// placeholders in config-example.yml represent secret values. For local development you can:
- Inline real values — simplest for a dev machine only you control.
-
Use
env://references — e.g.env://MY_JWT_SECRETreads the value from that environment variable at startup. -
Use
file://references — e.g.file:///run/secrets/jwt_secretreads from a file.
For a local dev machine, inlining values is the most straightforward option.
If the bootstrap key registry changes (for example, after a config refactor), regenerate config-example.yml to pick up new keys:
make generate-config-exampleCommit the updated config-example.yml. Do not commit the working file.
Any bootstrap setting can be overridden with a TMI_ environment variable. Environment variables take highest priority — they win over both the config file and the database.
The naming convention is TMI_ followed by the YAML key path in uppercase with dots replaced by underscores:
# Override the server port without editing the config file
export TMI_SERVER_PORT=9090
# Override the database URL
export TMI_DATABASE_URL="postgres://user:pass@localhost:5432/tmidev"
# Override the JWT secret
export TMI_JWT_SECRET="my-local-dev-secret"This is useful for quick iteration (testing a different port, pointing at a different database) without touching config-development.yml.
TMI requires OAuth authentication for all protected endpoints. For local development, use the built-in "tmi" OAuth provider together with the OAuth callback stub.
make start-oauth-stubStub logs are written to /tmp/oauth-stub.log. Stop it with:
make oauth-stub-stop# Start the stub (if not already running)
make start-oauth-stub
# Start an automated OAuth flow for user "alice"
curl -X POST http://localhost:8079/flows/start \
-H 'Content-Type: application/json' \
-d '{"userid": "alice"}'
# Wait a moment for the flow to complete, then retrieve the token
curl "http://localhost:8079/creds?userid=alice" | jq '.access_token'Use the returned access_token as a Bearer token in your API requests:
curl -H "Authorization: Bearer <token>" http://localhost:8080/threat_modelsThe "tmi" provider supports a login_hint query parameter that creates a predictable test user instead of a random one:
http://localhost:8080/oauth2/authorize?idp=tmi&login_hint=alice
- Creates
alice@tmi.localwith display nameAlice (TMI User) - Format: 3–20 characters, alphanumeric plus hyphens, case-insensitive
- Without
login_hint, you get a random user liketestuser-12345678@tmi.local -
login_hintis only available in the "tmi" provider — it does not exist in production builds
| User | Role | When to use |
|---|---|---|
charlie |
Administrator | Admin API calls, settings management |
alice |
Regular user | General development and testing |
bob |
Regular user | Multi-user scenarios |
On the first run against an empty database, TMI seeds all operational settings from the built-in registry defaults. You do not need to configure them manually to get a working server.
To inspect or change an operational setting after first run, use the /admin/settings admin API as the charlie admin user. For example, to check Timmy's configuration:
# Get a token for charlie
TOKEN=$(curl -s "http://localhost:8079/creds?userid=charlie" | jq -r '.access_token')
# List all settings (the endpoint takes no query parameters); filter for Timmy with jq
curl -s -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/admin/settings" | jq '[.[] | select(.key | startswith("timmy."))]'Timmy is the clearest example of a fully DB-backed subsystem: there is no timmy: block in the config file. To enable or configure Timmy in a local environment, update its timmy.* operational settings via the admin API. No server restart is needed — Timmy's access to content sources is toggled at runtime.
You can also inspect settings directly in the database — the /db skill runs queries against the local PostgreSQL instance (e.g. SELECT setting_key, value FROM system_settings WHERE setting_key LIKE 'timmy.%'). Editing should still go through the admin API so caching and encryption are handled correctly.
See Managing-Operational-Settings for a full guide to the admin settings API.
In development and test mode, TMI writes structured logs to:
logs/tmi.log
This file is in the project root directory. It is the first place to look when debugging unexpected server behavior. The log level is controlled by logging.level in config-development.yml or the TMI_LOG_LEVEL environment variable.
-
Configuration-Model — Deep dive into bootstrap vs. operational config and the
vault://secret reference format - Managing-Operational-Settings — How to inspect and update operational settings via the admin API
- Bootstrapping-Production — Equivalent guide for production deployments
- Getting-Started-with-Development — Cloning the repo, installing prerequisites, and the overall dev workflow
-
Local-Development-Cluster — The cluster-based
start-devonramp: prerequisites, kind/k3s, database targets (incl.DB=oracle), and the Tilt fast loop - Database-Setup — Detailed PostgreSQL container setup and schema migration
- Using TMI for Threat Modeling
- Accessing TMI
- Authentication
- Identity Linking
- Creating Your First Threat Model
- Understanding the User Interface
- Working with Data Flow Diagrams
- Managing Threats
- Collaborative Threat Modeling
- Using Notes and Documentation
- Timmy AI Assistant
- Metadata and Extensions
- Planning Your Deployment
- Terraform Deployment (AWS, OCI, GCP, Azure)
- Deploying TMI Server
- OCI Container Deployment
- Certificate Automation
- Deploying TMI Web Application
- Setting Up Authentication
- Database Setup
- Bootstrapping Production
- Component Integration
- Post-Deployment
- Branding and Customization
- Monitoring and Health
- Cloud Logging
- Configuring Local Development
- Managing Operational Settings
- Content Extractors - Limits and Overrides
- Database Operations
- Database Security Strategies
- Transaction Isolation
- Oracle Content Feedback FK Cleanup
- Security Operations
- Performance and Scaling
- Maintenance Tasks
- Getting Started with Development
- Local Development Cluster
- Architecture and Design
- API Integration
- Testing
- Contributing
- Extending TMI
- Dependency Upgrade Plans
- DFD Graphing Library Reference
- Migration Instructions
- Issue Tracker Integration
- Webhook Integration
- Addon System
- MCP Integration
- Delegated Content Providers
- Setting Up Google Content Providers
- API Clients
- API Client Maintenance
- Database Tool Reference
- TMI Terraform Analyzer
- TMI Promtail Logger
- WebSocket Test Harness