Skip to main content

Local Mode

Local mode gives you a full HatiData warehouse on your machine, powered by an embedded DuckDB engine. Your data stays on disk, queries run in-process, and no network connection is required.

How hati init Works

Running hati init creates a .hati/ directory in your current project:

hati init
Initialized HatiData warehouse in .hati/
Database: .hati/warehouse.duckdb
Config: .hati/config.toml

Under the hood, this:

  1. Creates a DuckDB database file at .hati/warehouse.duckdb
  2. Generates a default config.toml with sensible dev settings
  3. Sets up a local cache directory for transpilation results

.hati/ Directory Structure

.hati/
config.toml # Runtime configuration
warehouse.duckdb # DuckDB database file
warehouse.duckdb.wal # DuckDB write-ahead log (transient)
cache/
transpile/ # Cached SQL transpilation results
query/ # Cached query results (L2 NVMe cache)
snapshots/ # Iceberg-format table snapshots (if enabled)
Add .hati/ to .gitignore

The database file and cache contain your data. Do not commit them to source control.

Configuration Reference

The config.toml file controls how the local warehouse behaves:

.hati/config.toml
[database]
path = "warehouse.duckdb"
memory_limit_mb = 4096 # DuckDB memory limit
threads = 4 # DuckDB execution threads
max_concurrent_queries = 100 # Concurrent query limit

[transpiler]
auto_transpile = true # Automatically rewrite Snowflake SQL
cache_enabled = true # Cache transpilation results
dialect = "snowflake" # Source SQL dialect

[cache]
l1_max_entries = 10000 # In-memory cache entries
l1_ttl_seconds = 300 # In-memory cache TTL
l2_enabled = true # Disk-based L2 cache
l2_max_size_mb = 1024 # L2 cache size limit

[dev]
dev_mode = true # Relaxed auth, verbose logging
tls_enabled = false # No TLS for local development

Running Queries Offline

In local mode, all queries execute against the embedded DuckDB engine without any network calls:

# Create tables
hati query "CREATE TABLE orders (id INT, customer_id INT, total DECIMAL(10,2), created_at TIMESTAMP)"

# Load data from files
hati query "INSERT INTO orders SELECT * FROM read_csv('orders.csv', auto_detect=true)"

# Query with Snowflake-compatible SQL
hati query "SELECT NVL(customer_id, 0) AS cid, SUM(total) FROM orders GROUP BY 1"

Using psql Locally

You can also start the proxy locally and connect with any Postgres client:

# Start the MCP server as a local proxy (port 5439)
hatidata-mcp-server --local --port 5439

# In another terminal, connect with psql
psql -h localhost -p 5439 -U admin -d hatidata

# Run queries interactively
hatidata=> SELECT COUNT(*) FROM orders;
hatidata=> SELECT * FROM information_schema.tables;

Using Python Locally

from hatidata_agent import HatiDataAgent

agent = HatiDataAgent(
host="localhost",
port=5439,
agent_id="dev-agent",
framework="custom",
)

rows = agent.query("SELECT * FROM orders LIMIT 10")
for row in rows:
print(row)

Using TypeScript Locally

import { HatiDataClient } from '@hatidata/sdk';

const client = new HatiDataClient({
host: 'localhost',
port: 5439,
agentId: 'dev-agent',
framework: 'custom',
});

await client.connect();
const rows = await client.query('SELECT * FROM orders LIMIT 10');
console.log(rows);
await client.close();

Data Stays on Your Machine

In local mode:

  • The DuckDB database file is stored in .hati/warehouse.duckdb
  • No data is sent to any cloud service
  • No telemetry or metrics are collected
  • The transpilation cache stores SQL rewrites on disk, not query results
  • You can back up your data by copying the .hati/ directory

When you are ready to share your data or connect remote agents, use hati push --target cloud to migrate to Cloud Mode.

Loading Data

DuckDB's native file-reading functions work in local mode:

# CSV files
hati query "CREATE TABLE sales AS SELECT * FROM read_csv('sales.csv', auto_detect=true)"

# Parquet files
hati query "CREATE TABLE logs AS SELECT * FROM read_parquet('logs/*.parquet')"

# JSON files
hati query "CREATE TABLE events AS SELECT * FROM read_json('events.jsonl', auto_detect=true)"

Using with dbt

You can run dbt models against a local HatiData instance. Point your profiles.yml at localhost:

~/.dbt/profiles.yml
my_project:
target: dev
outputs:
dev:
type: hatidata
host: "localhost"
port: 5439
user: "admin"
password: ""
database: hatidata
schema: main
auto_transpile: true
threads: 4

Then run:

hatidata-mcp-server --local --port 5439   # Start the local proxy
dbt run # Run dbt models against local HatiData

See the dbt Adapter documentation for full configuration details.

Environment Variables

Local mode respects these environment variables when present:

VariableDefaultDescription
HATIDATA_DEV_MODEtrueEnables relaxed auth and verbose logging
HATIDATA_TLS_ENABLEDfalseEnables TLS on the proxy
HATIDATA_MEMORY_LIMIT_MB4096DuckDB memory limit
HATIDATA_THREADS4DuckDB execution threads
HATIDATA_MAX_CONCURRENT_QUERIES100Maximum concurrent queries
HATIDATA_AI_HEALER_ENDPOINT(unset)URL for AI query healing on failure

Environment variables override values in config.toml.

Next Steps

Stay in the loop

Product updates, engineering deep-dives, and agent-native insights. No spam.