Metacrafter Architecture Documentation
This page is the internals reference. For day-to-day usage see Getting started, Custom rules, and the CLI reference.
Table of Contents
- Overview
- High-Level Architecture
- Core Components
- Data Flow
- Rule System
- File Format Support
- Database Support
- API Server Architecture
- Configuration System
- Extension Points
- Performance Considerations
Overview
Metacrafter is a rule-based data classification engine designed to automatically label and classify fields in structured data sources. It identifies data types such as email addresses, phone numbers, person names, PII (Personally Identifiable Information), dates, identifiers (UUIDs, GUIDs), and many other semantic types.
Key Features
- Rule-Based Classification: Extensible YAML-based rule system with 111+ labeling rules
- Multi-Format Support: Handles CSV, JSON, JSONL, XML, Parquet, Avro, ORC, Excel, BSON, and more
- Database Integration: Supports SQL databases (via SQLAlchemy) and MongoDB
- Language & Context Awareness: Rules can be filtered by language and context (e.g., PII detection)
- Date Pattern Detection: 312+ date detection patterns using the
qddatelibrary - REST API Server: Built-in Flask-based API server for remote classification
- Registry Integration: Links to semantic data types registry at
registry.apicrafter.io
High-Level Architecture
┌────────────── ───────────────────────────────────────────────┐
│ CLI Interface (Typer) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ scan commands│ │ rules commands│ │server commands│ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
└─────────┼─────────────────┼─────────────────┼───────────────┘
│ │ │
└─────────────────┼─────────────────┘
│
┌─────────────────▼─────────────────┐
│ CrafterCmd (Core) │
│ - Orchestrates scanning │
│ - Handles I/O and formatting │
│ - Manages remote/local execution │
└─────────────────┬─────────────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌───────▼────────┐ ┌───────▼────────┐ ┌───────▼────────┐
│ RulesProcessor │ │ Analyzer │ │ DateParser │
│ - Rule loading│ │ - Statistics │ │ - Date patterns│
│ - Matching │ │ - Field stats │ │ - Pattern match│
│ - Filtering │ │ - Type detect │ │ │
└───────┬────────┘ └───────┬────────┘ └─────────────────┘
│ │
│ ┌─────────▼─────────┐
│ │ Data Sources │
│ │ - Files (iterable)│
│ │ - SQL databases │
│ │ - MongoDB │
│ └────────────────────┘
│
┌───────▼───────────────────────────────────────┐
│ Rule System (YAML) │
│ - Field rules (match field names) │
│ - Data rules (match data values) │
│ - PyParsing, text, function matchers │
└───────────────────────────────────────────────┘
Core Components
1. CLI Layer (metacrafter/core.py)
The CLI layer is built using Typer and provides three main command groups:
scan: Commands for scanning files, databases, and directoriesrules: Commands for inspecting and managing rulesserver: Commands for running the API server
Entry Point: metacrafter/__main__.py → core.app()
2. CrafterCmd Class (metacrafter/core.py)
The central orchestrator class that coordinates all operations:
class CrafterCmd:
- prepare() # Load rules and initialize processors
- scan_file() # Scan data files
- scan_db() # Scan SQL databases
- scan_mongodb() # Scan MongoDB databases
- scan_data() # Scan in-memory data structures
- scan_bulk() # Scan multiple files in directory
- rules_list() # List all loaded rules
- rules_dumpstats() # Show rule statistics
Key Responsibilities:
- Configuration management (rule paths, country codes, etc.)
- Format conversion (table, JSON, YAML, CSV)
- Progress reporting (via
tqdm) - Remote API integration
- Output formatting and writing
3. RulesProcessor (metacrafter/classify/processor.py)
The core classification engine that loads, compiles, and applies rules:
class RulesProcessor:
- import_rules_path() # Load rules from directory
- import_rules() # Load rules from YAML file
- match_dict() # Classify fields in dictionary/list
- match_field() # Match field name rules
- match_data() # Match data value rules
Key Features:
- Rule Types: Field rules (match column names) and Data rules (match values)
- Match Engines:
text: Exact text matching (case-insensitive)ppr: PyParsing pattern matchingfunc: Python function-based matching
- Filtering: Language, context, and country code filtering
- Confidence Scoring: Calculates match confidence based on sample data
Rule Compilation:
- PyParsing rules are compiled with security restrictions (no dangerous code)
- Function rules are dynamically imported from modules
- Text rules are converted to optimized sets for O(1) lookup
4. Analyzer (metacrafter/classify/stats.py)
Generates field-level statistics for data analysis:
class Analyzer:
- analyze() # Analyze data and generate statistics
- guess_datatype() # Infer data type from value
Statistics Generated:
- Field type (
str,int,float,bool,date, etc.) - Length statistics (min, max, average)
- Uniqueness metrics (unique count, uniqueness percentage)
- Dictionary detection (low-cardinality fields)
- Character analysis (digits, alphabetic, special characters)
- Empty value detection
5. Date Parser (qddate integration)
Uses the qddate library for date pattern detection:
- 312+ date/time patterns
- Supports English and Russian date formats
- Pattern matching with confidence scoring
- Format extraction (e.g., "YYYY-MM-DD", "DD/MM/YYYY")
6. Configuration System (metacrafter/config.py)
Manages configuration loading and validation:
class ConfigLoader:
- load_config() # Load from .metacrafter file
- get_rulepath() # Get validated rule paths
Configuration Sources (in order of precedence):
- Command-line arguments (
--rulepath) - Local
.metacrafterfile (current directory) - Home directory
~/.metacrafterfile - Default:
["rules"]
Configuration Format:
rulepath:
- ./rules
- ./custom_rules
- /path/to/additional/rules
Data Flow
File Scanning Flow
1. User invokes: metacrafter scan file data.csv
│
▼
2. CLI parses arguments → CrafterCmd instance created
│
▼
3. CrafterCmd.prepare()
├─→ ConfigLoader.get_rulepath()
├─→ RulesProcessor.import_rules_path() (loads all YAML rules)
└─→ DateParser initialization
│
▼
4. CrafterCmd.scan_file()
├─→ Detect file format (via iterabledata)
├─→ Open file with appropriate reader
│
▼
5. For each batch of records:
├─→ Analyzer.analyze() → Generate field statistics
└─→ RulesProcessor.match_dict() → Classify fields
├─→ Match field names (field rules)
├─→ Match data values (data rules)
└─→ Match date patterns (qddate)
│
▼
6. Aggregate results → Calculate confidence scores
│
▼
7. Format output (table/JSON/YAML/CSV) → Write to file/stdout
Database Scanning Flow
1. User invokes: metacrafter scan sql "postgresql://..."
│
▼
2. CrafterCmd.scan_db()
├─→ SQLAlchemy connection → List tables/schemas
│
▼
3. For each table:
├─→ Query rows in batches (batch_size)
├─→ Convert rows to dict format
│
▼
4. Process like file scanning:
├─→ Analyzer.analyze()
└─→ RulesProcessor.match_dict()
│
▼
5. Aggregate per-table results
│
▼
6. Format multi-table output → Write results
Rule Matching Flow
1. RulesProcessor.match_dict(items, datastats, ...)
│
▼
2. For each field in data:
│
├─→ Field Name Matching (if field rules enabled)
│ ├─→ Check field_rules list
│ ├─→ Apply text/ppr/func matchers
│ └─→ If match → Add RuleResult
│
├─→ Data Value Matching (if data rules enabled)
│ ├─→ Sample values (up to limit)
│ ├─→ For each data rule:
│ │ ├─→ Check length constraints (minlen/maxlen)
│ │ ├─→ Apply match function (text/ppr/func)
│ │ ├─→ Run validator if present
│ │ └─→ Calculate match percentage
│ └─→ If confidence > threshold → Add RuleResult
│
└─→ Date Pattern Matching (if enabled)
├─→ For each value → qddate.match()
└─→ If pattern found → Add RuleResult with format
│
▼
3. Calculate confidence scores:
├─→ Match percentage = (matches / total_samples) * 100
├─→ Apply imprecise rule penalties
└─→ Filter by confidence threshold
│
▼
4. Return TableScanResult with ColumnMatchResult per field
Rule System
Rule File Structure
Rules are organized in YAML files with the following structure:
name: common # Rule group name
description: Common data types # Description
context: common # Context filter (common, pii, etc.)
lang: common # Language code (en, ru, fr, common, etc.)
country_code: us,ca # Optional: ISO country codes (comma-separated)
rules:
rule_id: # Unique rule identifier
key: datatype_key # Data type identifier (e.g., "email", "phone")
name: Human readable name # Display name
type: field|data # Rule type: field (match name) or data (match value)
match: text|ppr|func # Match engine type
rule: <rule_definition> # Rule definition (depends on match type)
priority: 1 # Priority (higher = more important)
minlen: 3 # Minimum value length
maxlen: 100 # Maximum value length
imprecise: 0|1 # Whether rule is imprecise (default: 0)
is_pii: true|false # Whether this is PII data
validator: module.func # Optional: validation function
fieldrule: <rule> # Optional: additional field name rule
fieldrulematch: text|ppr # Match type for fieldrule
Rule Types
1. Field Rules (type: field)
Match based on column/field names:
email:
key: email
name: Email by known field name
type: field
match: text
rule: email,e_mail,email_address,emailAddress
Use Cases: Identifying columns by common naming conventions
2. Data Rules (type: data)
Match based on data values:
Text Matching:
mimetypebyvalue:
key: mimetype
name: Mimetype by value
type: data
match: text
rule: application/pdf,application/json,image/jpeg
PyParsing Matching:
ru_kadastr:
key: rukadastr
name: Russian cadastral number
type: data
match: ppr
rule: Word(nums, min=1, max=2) + Literal(':').suppress() + Word(nums, min=1, max=2) + ...
Function Matching:
runpabyfunc:
key: runpa
name: Russian legal act / law
type: data
match: func
rule: metacrafter.rules.ru.gov.is_ru_law
Rule Organization
Rules are organized in directories by language and context:
rules/
├── basic/ # Basic identifiers (UUID, crypto, etc.)
├── common/ # Common data types (email, phone, URLs)
├── en/ # English-specific rules
├── fr/ # French-specific rules
├── ru/ # Russian-specific rules
├── pii/ # PII detection rules
│ ├── pii.yaml
│ ├── fr/
│ └── ru/
└── ...
Rule Filtering
Rules can be filtered at load time or runtime:
- Language Filtering:
--langs en,ru→ Only load rules for English and Russian - Context Filtering:
--contexts pii→ Only load PII-related rules - Country Filtering:
--country-codes us,ca→ Only load US/Canada rules
Rule Compilation Security
PyParsing rules are compiled with security restrictions:
- No
import,exec,eval,compilestatements - No access to
__builtins__except safe functions (len,str,int, etc.) - Restricted namespace with only PyParsing classes
- Cached compilation results (
@lru_cache)
File Format Support
Metacrafter uses the iterabledata package for file format support, providing automatic format detection and unified iteration interface.
Supported Formats
Text Formats:
- CSV, TSV (with auto-delimiter detection)
- JSON (array of objects)
- JSONL/NDJSON (newline-delimited JSON)
- XML (with configurable tag name)
Binary Formats:
- Parquet (Apache Parquet)
- Avro (Apache Avro)
- ORC (Apache ORC)
- Excel (
.xls,.xlsx) - BSON (Binary JSON)
- Pickle (
.pickle,.pkl)
Compression Codecs (auto-detected):
- gzip (
.gz) - bzip2 (
.bz2) - xz (
.xz) - lz4 (
.lz4) - zstandard (
.zst) - Brotli (
.br) - Snappy
- ZIP (
.zip)
Format Detection
Format detection is automatic based on file extension:
data.csv.gz→ Detected as gzip-compressed CSVdata.jsonl.bz2→ Detected as bzip2-compressed JSONL- Compression and underlying format are both detected
File Reading Flow
# In CrafterCmd.scan_file()
from iterable.helpers.detect import open_iterable
with open_iterable(filename, **options) as reader:
for record in reader:
# Process each record as dict
process_record(record)
Database Support
SQL Databases (SQLAlchemy)
Metacrafter supports any database with SQLAlchemy support:
- PostgreSQL:
postgresql+psycopg2://... - MySQL/MariaDB:
mysql+pymysql://... - SQLite:
sqlite:///path/to/db - SQL Server:
mssql+pyodbc://... - Oracle:
oracle+cx_oracle://... - DuckDB:
duckdb:///path/to/db
Scanning Process:
- Connect via SQLAlchemy connection string
- List tables (optionally filtered by schema)
- For each table:
- Query rows in batches (
batch_size) - Convert to dict format (column name → value)
- Process like file data
- Query rows in batches (
Batch Processing:
- Configurable batch size (default: 1000 rows)
- Progress reporting with
tqdm - Memory-efficient streaming
MongoDB
MongoDB support via pymongo:
Connection Methods:
- Host/port:
scan_mongodb(host="localhost", port=27017) - Connection URI:
scan_mongodb(host="mongodb://...")
Scanning Process:
- Connect to MongoDB
- List collections in database
- For each collection:
- Fetch documents in batches
- Process documents (already dict-like)
- Classify fields
Features:
- Authentication support (username/password)
- Replica set support
- Batch cursor configuration
API Server Architecture
Server Components
MetacrafterApp (metacrafter/server/api.py):
- Flask application factory
- Dependency injection for rules processor and date parser
- Lazy initialization of heavy components
Server Manager (metacrafter/server/manager.py):
- Server startup and configuration
- Secret key management (from environment)
- Debug logging configuration
API Endpoints
POST /api/v1/scan_data:
- Accepts JSON array of items (list of dicts)
- Query parameters:
format: Output format (short/full)langs: Comma-separated language filterscontexts: Comma-separated context filterslimit: Maximum records per field (default: 1000)
- Returns JSON with classification results
Request Example:
[
{"email": "user@example.com", "name": "John Doe"},
{"email": "admin@example.com", "name": "Jane Smith"}
]
Response Example:
{
"results": [
["email", "str", "", "email 98.50", "https://registry.apicrafter.io/datatype/email"],
["name", "str", "", "name 100.00", "https://registry.apicrafter.io/datatype/name"]
],
"data": [
{
"field": "email",
"matches": [...],
"ftype": "str",
"tags": [],
"datatype_url": "https://registry.apicrafter.io/datatype/email",
"stats": {...}
}
]
}
Remote Scanning
CLI can use remote API server:
metacrafter scan file data.csv --remote http://localhost:10399
Flow:
- CLI detects
--remoteflag - Data is serialized to JSON
- POST request to
/api/v1/scan_data - Response is parsed and formatted locally
Benefits:
- Centralized rule management
- Resource sharing
- API integration
Configuration System
Configuration Loading Order
-
Command-line arguments (highest priority)
--rulepath ./custom_rules--country-codes us,ca
-
Local
.metacrafterfile (current directory)rulepath:- ./rules- ./custom_rules -
Home directory
~/.metacrafter(user-wide config) -
Defaults (lowest priority)
rulepath: ["rules"]
Configuration Validation
Configuration is validated using Pydantic:
- Type checking
- Path existence validation
- Error messages for invalid configs
Environment Variables
METACRAFTER_SECRET_KEY: Secret key for API server (optional)
Extension Points
Adding Custom Rules
- Create YAML file in rules directory:
name: custom
description: Custom rules
context: common
lang: common
rules:
my_rule:
key: custom_type
name: Custom data type
type: data
match: ppr
rule: Word(alphas, min=3, max=10)
- Add to rulepath (config file or CLI):
metacrafter scan file data.csv --rulepath ./rules,./custom_rules
Adding Custom Validators
Validators are Python functions that validate matched values:
# In your module
def validate_custom_type(value: str) -> bool:
# Custom validation logic
return len(value) > 5 and value.isalnum()
# In rule YAML
my_rule:
validator: mymodule.validators.validate_custom_type
Adding Custom Match Functions
# In your module
def match_custom_pattern(value: str) -> bool:
# Custom matching logic
return value.startswith("CUSTOM_")
# In rule YAML
my_rule:
match: func
rule: mymodule.matchers.match_custom_pattern
Adding File Format Support
File format support is handled by iterabledata. To add support:
- Ensure
iterabledatasupports the format - Metacrafter will automatically detect and use it
Performance Considerations
Rule Compilation Caching
- PyParsing rules are cached with
@lru_cache(maxsize=256) - Reduces recompilation overhead for repeated rules
Batch Processing
- Database queries use configurable batch sizes
- Default: 1000 rows/documents per batch
- Reduces memory usage for large datasets
Sampling
- Data rules only sample up to
limitvalues per field (default: 1000) - Reduces processing time for large datasets
- Confidence scores are calculated from samples
Progress Reporting
- Optional
tqdmprogress bars - Can be disabled with
--quietflag - Minimal overhead when disabled
Memory Management
- Streaming file readers (via
iterabledata) - Batch-based database queries
- Results are aggregated incrementally
Remote API Considerations
- Network latency for remote scans
- Configurable timeout and retry logic
- Batch size affects API payload size
Key Design Decisions
1. Rule-Based Architecture
Why: Extensibility and maintainability
- Rules are declarative (YAML)
- Easy to add new rules without code changes
- Rules can be versioned and shared
2. Multiple Match Engines
Why: Flexibility for different use cases
- Text matching: Fast, exact matches
- PyParsing: Complex pattern matching
- Functions: Custom logic
3. Confidence Scoring
Why: Handle ambiguous matches
- Percentage-based confidence (0-100)
- Threshold filtering
- Multiple matches per field
4. Language & Context Filtering
Why: Performance and relevance
- Reduce rule set size
- Focus on relevant rules
- Support multi-language datasets
5. Unified Data Interface
Why: Code reuse across formats
- All sources convert to dict/list format
- Single processing pipeline
- Consistent output format
6. Security in Rule Compilation
Why: Prevent code injection
- Restricted namespace for PyParsing
- Function imports are controlled
- No arbitrary code execution
Future Architecture Considerations
Potential Enhancements
- Rule Versioning: Track rule versions and changes
- Rule Testing Framework: Unit tests for rules
- Distributed Processing: Support for distributed scanning
- Rule Marketplace: Share and discover rules
- Machine Learning Integration: ML-based classification alongside rules
- Incremental Scanning: Track changes in data sources
- Rule Performance Metrics: Track rule execution time and accuracy
Dependencies
Core Dependencies
- typer: CLI framework
- pyparsing: Pattern matching engine
- PyYAML: Rule file parsing
- pydantic: Configuration validation
- tabulate: Table formatting
- iterabledata: File format support
- qddate: Date pattern detection
- sqlalchemy: SQL database support
- pymongo: MongoDB support
Optional Dependencies
- tqdm: Progress bars
- orjson: Fast JSON parsing (used in stats module)
Conclusion
Metacrafter's architecture is designed for:
- Extensibility: Easy to add new rules and formats
- Performance: Efficient processing of large datasets
- Flexibility: Multiple interfaces (CLI, API, Python library)
- Security: Safe rule compilation and execution
- Maintainability: Clear separation of concerns
The modular design allows components to evolve independently while maintaining a consistent interface for users and developers.