Summary
This is a part of Chronos tutorial series.
Chronos by Intelligex is an open source, self-hosted control plane for AI agents and MCP tools — application state and data lives on your own infrastructure. This tutorial covers exactly where that state lives: the .chronos directory on disk, the relational database backend, encryption-at-rest for secrets, the blob storage layer for uploaded files, and the log-file layout. It applies equally to local development and production-shaped deployments.
Overview
Chronos persists data in two primary locations:
- The
.chronosdirectory — a local filesystem directory holding the SQLite database (when used), the encryption key, file uploads, and logs. - The relational database — a TypeORM-backed schema supporting SQLite and PostgreSQL.
This split lets developers start with zero configuration (SQLite + local filesystem) and scale up to production-grade backends (Postgres + S3) by changing environment variables, not code.
The .chronos directory
Automatic initialisation
When Chronos starts, it creates .chronos/ in the user's home directory if it doesn't already exist:
// DataSource.ts
let chronosPath = path.join(getUserHome(), '.chronos');
if (!fs.existsSync(chronosPath)) {
fs.mkdirSync(chronosPath);
}
Directory layout
~/.chronos/
├── database.sqlite # SQLite database (default)
├── encryption.key # AES key for credential encryption
├── storage/ # Blob storage for uploaded files
│ └── {orgId}/
│ └── {agentflowId}/
│ └── {chatId}/
│ └── {files}
└── logs/ # Application logs (when LOG_PATH is unset)
├── server.log
├── server-error.log
└── server-requests.log.jsonl
Customise paths via environment variables
Every path can be overridden — useful for containerised or cloud deployments where you want state on a mounted volume:
| Environment variable | Default | Purpose |
|---|---|---|
DATABASE_PATH | ~/.chronos/ | Directory containing database.sqlite |
SECRETKEY_PATH | ~/.chronos/ | Directory containing encryption.key |
BLOB_STORAGE_PATH | ~/.chronos/storage | Local blob storage root |
LOG_PATH | ~/.chronos/logs | Application logs |
Database architecture
Supported backends
Chronos uses TypeORM as its ORM layer. These backends ship out of the box:
- SQLite (default) — zero-configuration, file-based.
- PostgreSQL — recommended for production type deployments.
Selection is controlled via DATABASE_TYPE:
switch (process.env.DATABASE_TYPE) {
case 'sqlite': // ~/.chronos/database.sqlite
case 'postgres': // DATABASE_HOST, DATABASE_PORT, …
default: // falls back to SQLite
}
Entity schema
Chronos defines 22 database entities that model the entire application state. Two of those — Agent and MCPServer — were recently added as part of the Agent Registry and MCP Gateway work.
Core flow entities
| Entity | Purpose | Key fields |
|---|---|---|
ChatFlow | Agentflow / canvas configurations | id, name, flowData (JSON), deployed, type |
ChatMessage | Conversation messages | role, content, sourceDocuments, usedTools, agentReasoning |
ChatMessageFeedback | User feedback on responses | chatflowid, messageId, rating, content |
Execution | Flow execution records | Links messages to execution context |
Agent registry and MCP gateway
| Entity | Purpose | Key fields |
|---|---|---|
Agent | Registered agents — both BUILT_IN (canvas) and HTTP (external) runtime types | name, slug, runtimeType, serviceEndpoint, outboundAuth, callbackToken, allowedTools, status |
MCPServer | Registered MCP servers brokered through the Chronos gateway | name, slug, transport, url, outboundAuth, allowedTools, status, requestHeaders |
Both rows are BUILT_IN-flavoured for canvas agents and operator-created for HTTP agents and MCP servers.
Security and configuration
| Entity | Purpose | Key fields |
|---|---|---|
Credential | Encrypted API keys | name, credentialName, encryptedData |
Variable | Global variables | name, value, type (string/runtime) |
ApiKey | External access API keys | Authentication tokens for API access |
User | User accounts | Authentication and authorisation |
Document management
| Entity | Purpose | Key fields |
|---|---|---|
DocumentStore | Document store metadata | name, description, loaders, vectorStoreConfig |
DocumentStoreFileChunk | Indexed document chunks | Individual chunks for vector stores |
UpsertHistory | Vector indexing history | Tracks document upsert operations |
AI components
| Entity | Purpose | Key fields |
|---|---|---|
Tool | Custom tool configurations | Tool definitions for agents |
Assistant | AI assistant configs | Assistant configurations |
CustomTemplate | Message templates | Reusable prompt templates |
Evaluation and analytics
| Entity | Purpose | Key fields |
|---|---|---|
Dataset | Evaluation datasets | Dataset metadata |
DatasetRow | Dataset entries | Individual test cases |
Evaluation | Evaluation configurations | Evaluation setup |
EvaluationRun | Evaluation executions | Run results |
Evaluator | Evaluator definitions | Evaluation criteria |
Lead | Lead capture data | Captured user information |
Schema migrations
Schema evolution is handled through TypeORM migrations, organised per backend:
packages/server/src/database/migrations/
├── sqlite/
├── postgres/
Migrations run controllably on startup.
File storage architecture
Storage backends
Chronos supports three blob backends for uploaded files:
- Local (default) — files stored under
~/.chronos/storage/. - S3 compatible — AWS S3 or any S3-compatible service (R2, MinIO, etc.).
- GCS — Google Cloud Storage.
Selection is controlled via STORAGE_TYPE:
export const getStorageType = (): string => {
return process.env.STORAGE_TYPE ? process.env.STORAGE_TYPE : 'local';
};
Credential encryption
Credentials (LLM API keys, MCP server tokens, agent outbound-auth secrets) are stored as ciphertext in the Credential table. Plaintext values do not reach the databases.
The encryption key lives at ~/.chronos/encryption.key (can be owerritten by SECRETKEY_PATH env variable).
On first start, Chronos generates a fresh key if none exists. Treat this file like any other production secret — back it up, rotate when compromised, and ensure the path is on a mounted volume in any containerised deployment so the key persists across restarts.
Logging architecture
Winston with daily rotation
Logging uses Winston with file rotation:
server.log— general application logs.server-error.log— error-level logs.server-requests.log.jsonl— HTTP request logs in JSONL format.
Log levels
Supported log levels: DEBUG, VERBOSE, INFO, WARN, ERROR.
Sensitive-data sanitisation
Sensitive fields are stripped from logs before they're written:
- Passwords and secrets.
- API keys and tokens.
- Authorization headers.
The same pipeline emits the audit lines from the MCP gateway event=mcp.tool.invoke — pipe these into your existing log aggregator. Chronos will introduce the persistent tool_invocation_audit concept in the near future.
Next steps
- Database for production. Postgres is the recommended default; SQLite is fine for single-host or development. Hovever, some users have managed to productionise SQLite setup in production type environments as well.
- Mount your state. In Docker / Kubernetes, mount
~/.chronos/or overrideDATABASE_PATH,SECRETKEY_PATH,BLOB_STORAGE_PATHonto persistent storage so it survives container restarts. - Move blobs off the local disk. Set
STORAGE_TYPE=s3(orgcs) for any deployment with more than one Chronos instance — local-disk storage can't be shared across replicas.