Overview
Geode provides a comprehensive, security-first audit logging and tracing subsystem designed for enterprise compliance and operational security. The system instruments authentication actions and database operations, emitting structured logs with tamper-evident guarantees through cryptographic hash chains and digital signatures.
Key Features
Security-First Design:
- Default-deny filtration with built-in sensitive data redaction
- Tamper-evident logging with hash chains and Ed25519 signatures
- Fail-closed security: drops events if redaction cannot guarantee safety
- No SQL exposure: database adapter never logs query text or parameters
Enterprise Integration:
- RFC 5424 Syslog with UDP/TCP transport
- CEF:0 (Common Event Format) payloads for SIEM compatibility
- Structured JSON-Lines format with deterministic field ordering
- File rotation with configurable retention and backup policies
Operational Excellence:
- Non-blocking operations with bounded queues
- W3C distributed tracing support (trace_id and span_id propagation)
- Automatic field detection and PII/PCI redaction
- <500μs append latency (excluding fsync)
Architecture
Components
AuditLogger
├── EventBuffer - Bounded queue for non-blocking writes
├── FileRotator - Size/time-based rotation with backups
├── HashChain - SHA-256/BLAKE3 chain with prev_hash linking
├── SignatureManager - Ed25519 signing for Merkle digest records
├── RedactionEngine - Multi-layer sensitive data protection
└── SyslogTransport - RFC 5424 UDP/TCP syslog integration
Data Flow
1. Application Event → AuditLogger.audit()
2. Redaction Engine → Strip PII/PCI, apply default-deny filters
3. Event Buffer → Non-blocking enqueue (drop on overflow)
4. Hash Chain → Compute event_hash, link with prev_hash
5. File Append → Write canonical JSON line (0600 permissions)
6. Signature Manager → Every 100 events, compute Merkle digest + sign
7. Syslog Transport → Send to remote syslog server (parallel)
8. File Rotation → Rotate on size threshold, preserve chain continuity
Hash Chain & Verification
Tamper-Evident Design
Geode’s audit stream is a canonical JSON Lines (JSONL) append-only log where each record carries:
- seq_no: Monotonically increasing sequence number
- event_hash: SHA-256 hash of current event (excluding prev_hash)
- prev_hash: Hash of previous event’s event_hash
- signature: Ed25519 signature on Merkle digest (every 100 events)
This creates a cryptographic chain: modifying any past event breaks the chain and invalidates all subsequent signatures.
Record Structure
Canonical Key Order:
[
"stream", "seq_no", "timestamp", "event_type",
"actor", "origin", "resource", "action", "result",
"details", "prev_hash", "event_hash", "signature"
]
Example Event:
{
"stream": "geode-audit",
"seq_no": 42,
"timestamp": "2026-01-24T10:30:15.123Z",
"event_type": "auth.login",
"actor": "[email protected]",
"origin": "192.168.1.100",
"resource": "database:geode",
"action": "authenticate",
"result": "success",
"details": {"role": "admin", "session_id": "ses-abc123"},
"prev_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"event_hash": "a3c7f1d2e5b4a8c9f0d1e2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3",
"signature": null
}
Digest Record (every 100 events):
{
"stream": "geode-audit",
"seq_no": 100,
"timestamp": "2026-01-24T10:35:00.000Z",
"event_type": "system.digest",
"merkle_root": "b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5",
"signature": "d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9..."
}
Hash Algorithm
Default: SHA-256 (widely supported, FIPS 140-2 compliant)
Alternative: BLAKE3 (faster, modern cryptographic standard)
Chain Logic:
- First event:
prev_hash = 0x00000000...(32 zero bytes) - Subsequent events:
prev_hash = hash(previous_event.event_hash) - Digest events: Compute Merkle root over last N event_hashes
- Sign Merkle root with Ed25519 private key
Signing Configuration
Production Requirements:
# Generate Ed25519 key pair
openssl genpkey -algorithm ED25519 -out audit_private.pem
openssl pkey -in audit_private.pem -pubout -out audit_public.pem
# Convert to hex format for environment variables
GEODE_AUDIT_PUB=$(openssl pkey -in audit_public.pem -pubin -text | grep -A4 pub | tail -n +2 | tr -d ':' | tr -d '\n')
GEODE_AUDIT_SEC=$(openssl pkey -in audit_private.pem -text | grep -A4 priv | tail -n +2 | tr -d ':' | tr -d '\n')
export GEODE_AUDIT_PUB
export GEODE_AUDIT_SEC
Startup Validation:
- If keys missing in non-test builds: Exit with
CONFIG_ERROR SIGNING_KEYS_MISSING(exit code 78) - Test builds: Derive deterministic key from fixed seed (for reproducible tests)
Key Rotation:
{
"event_type": "system.key_rotation",
"details": {
"old_key_fingerprint": "sha256:abc123...",
"new_key_fingerprint": "sha256:def456...",
"rotation_timestamp": "2026-01-24T12:00:00.000Z"
}
}
Configuration
JSON Configuration Schema
Location: config/logging.json
{
"version": 1,
"service": {
"name": "geode-server",
"host": "prod-db-01",
"pid": 1234
},
"identity": {
"cef": {
"vendor": "DEVNW",
"product": "GEODE",
"version": "0.1.3"
}
},
"sinks": {
"file": {
"enabled": true,
"path": "/var/log/geode/audit.jsonl",
"max_bytes": 104857600,
"backup_count": 10
},
"syslog": {
"enabled": true,
"protocol": "udp",
"host": "syslog.example.com",
"port": 514,
"rfc5424": {
"facility": "local4"
}
}
},
"filters": {
"default_deny": true,
"allow_fields": [
"actor_id", "subject_id", "request_id", "session_id",
"source_ip", "trace_id", "span_id",
"category", "action", "outcome", "severity",
"db.operation", "db.table", "db.rowCount", "duration_ms"
],
"deny_key_patterns": ["password", "secret", "token", "key"],
"pii_mode": "mask",
"pci_mode": true
},
"hash_chain": {
"algorithm": "sha256",
"digest_interval": 100
},
"signing": {
"enabled": true,
"algorithm": "ed25519"
}
}
Configuration Options
Service Identity:
name: Service name for audit trail correlationhost: Hostname for distributed deployment identificationpid: Process ID (auto-populated at runtime)
File Sink:
path: Audit log file path (created with 0600 permissions)max_bytes: Rotation threshold (default: 100 MB)backup_count: Number of rotated files to keep (default: 10)
Syslog Sink:
protocol: “udp” or “tcp”host: Syslog server hostnameport: Syslog server port (514 for UDP, 6514 for TLS)facility: RFC 5424 facility (default: “local4”)
Filters:
default_deny: If true, onlyallow_fieldsare loggedallow_fields: Explicit allowlist of field namesdeny_key_patterns: Regex patterns for sensitive keyspii_mode: “mask”, “redact”, or “allow”pci_mode: Enable PCI-DSS compliant credit card redaction
Hash Chain:
algorithm: “sha256” or “blake3”digest_interval: Events between Merkle digest records
Signing:
enabled: Generate Ed25519 signatures (default: true)algorithm: “ed25519” (currently only supported algorithm)
Event Types
Authentication Events
Successful Login:
{
"event_type": "auth.login",
"actor": "[email protected]",
"action": "authenticate",
"result": "success",
"details": {
"role": "admin",
"session_id": "ses-abc123",
"auth_method": "password"
}
}
Failed Login:
{
"event_type": "auth.login",
"actor": "[email protected]",
"action": "authenticate",
"result": "failure",
"details": {
"reason": "invalid_credentials",
"attempts": 3
}
}
Logout:
{
"event_type": "auth.logout",
"actor": "[email protected]",
"action": "terminate_session",
"result": "success",
"details": {
"session_id": "ses-abc123",
"session_duration_ms": 3600000
}
}
Database Operations
Query Execution:
{
"event_type": "db.query",
"actor": "[email protected]",
"resource": "graph:main",
"action": "execute",
"result": "success",
"details": {
"db.operation": "MATCH",
"db.rowCount": 42,
"duration_ms": 15,
"trace_id": "trace-xyz789"
}
}
Schema Modification:
{
"event_type": "db.schema_change",
"actor": "[email protected]",
"resource": "index:user_email",
"action": "create",
"result": "success",
"details": {
"index_type": "btree",
"columns": ["email"]
}
}
Access Denied:
{
"event_type": "db.access_denied",
"actor": "[email protected]",
"resource": "node:Person",
"action": "create",
"result": "failure",
"details": {
"reason": "insufficient_permissions",
"required_role": "ReadWrite",
"actual_role": "ReadOnly"
}
}
System Events
Server Startup:
{
"event_type": "system.startup",
"actor": "system",
"action": "initialize",
"result": "success",
"details": {
"version": "0.1.3",
"config_file": "/etc/geode/config.yaml"
}
}
File Rotation:
{
"event_type": "system.rotation",
"actor": "system",
"action": "rotate_audit_log",
"result": "success",
"details": {
"old_file": "/var/log/geode/audit.jsonl",
"new_file": "/var/log/geode/audit.jsonl.1",
"size_bytes": 104857600
}
}
Sensitive Data Redaction
Multi-Layer Protection
Geode employs five layers of protection against sensitive data leakage:
Key Pattern Detection
- Automatic detection of sensitive key names
- Patterns:
password,secret,token,key,credential,ssn,card
Value Analysis
- Credit card number detection (Luhn algorithm)
- Social Security Number patterns
- Email address masking
- IP address anonymization
Default-Deny Filtering
- Only
allow_fieldsare logged - Unknown fields automatically dropped
- Explicit opt-in required for new fields
- Only
Fail-Closed Behavior
- If redaction cannot guarantee safety, drop entire event
- Log warning:
AUDIT_REDACTION_FAILED - Never log potentially sensitive data
Database Adapter Restriction
- SQL text and parameters never accepted
- Only metadata logged: operation type, row count, duration
- Prevents accidental query parameter exposure
Redaction Examples
Original Event:
{
"user": "[email protected]",
"password": "SecurePassword123!",
"credit_card": "4111-1111-1111-1111",
"ssn": "123-45-6789"
}
Redacted Event:
{
"user": "[email protected]",
"password": "***REDACTED***",
"credit_card": "***REDACTED***",
"ssn": "***REDACTED***"
}
PII Mode: Mask:
{
"email": "a***@e*****.com",
"ip_address": "192.168.xxx.xxx"
}
PII Mode: Redact:
{
"email": "***REDACTED***",
"ip_address": "***REDACTED***"
}
File Management
Rotation Strategy
Size-Based Rotation:
/var/log/geode/audit.jsonl (current, <100 MB)
/var/log/geode/audit.jsonl.1 (100 MB, yesterday)
/var/log/geode/audit.jsonl.2 (100 MB, 2 days ago)
...
/var/log/geode/audit.jsonl.10 (100 MB, 10 days ago)
Rotation Process:
- Current file reaches
max_bytesthreshold - Close current file
- Rename:
audit.jsonl→audit.jsonl.1 - Rotate existing:
.1→.2,.2→.3, etc. - Delete oldest:
audit.jsonl.{backup_count}removed - Create new:
audit.jsonlwith 0600 permissions - Continue
seq_noandprev_hashchain from previous file - Emit
system.rotationevent
Chain Continuity:
- First record in new file uses
prev_hashfrom tail of previous file - Maintains tamper-evident chain across file boundaries
- Verification tool must process files in chronological order
Permissions & Security
File Permissions:
# Audit log files
-rw------- 1 geode geode 104857600 Jan 24 10:30 audit.jsonl
# Directory permissions
drwx------ 2 geode geode 4096 Jan 24 10:30 /var/log/geode/
SELinux Context (if enabled):
chcon -t geode_log_t /var/log/geode/audit.jsonl
AppArmor Profile:
/var/log/geode/audit.jsonl rw,
/var/log/geode/audit.jsonl.* r,
Verification
Audit Log Verification
Verify tamper-evident chain and signatures:
# Verify single file
geode audit verify \
--file /var/log/geode/audit.jsonl \
--algo sha256 \
--pubkey $GEODE_AUDIT_PUB
# Verify rotated files (chronological order)
for file in $(ls -tr /var/log/geode/audit.jsonl*); do
geode audit verify \
--file $file \
--algo sha256 \
--pubkey $GEODE_AUDIT_PUB
done
Verification Outputs:
✅ Valid Chain:
OK chain records=1000 digests=10 algo=sha256
❌ Tampered Chain:
TAMPER chain at_seq=523 reason=PREV_HASH_MISMATCH
expected=a3c7f1d2e5b4a8c9f0d1e2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3
got=b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5
❌ Invalid Signature:
TAMPER digest at_seq=600 reason=DIGEST_MISMATCH
expected=d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9
got=e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0
⚠️ Schema Error:
SCHEMA_ERROR at_seq=789 field=timestamp detail="missing required field"
WARN ignored trailing partial record
Forensic Analysis
Extract events by actor:
jq 'select(.actor == "[email protected]")' /var/log/geode/audit.jsonl
Count events by type:
jq -r '.event_type' /var/log/geode/audit.jsonl | sort | uniq -c | sort -rn
Find failed authentications:
jq 'select(.event_type == "auth.login" and .result == "failure")' \
/var/log/geode/audit.jsonl
Verify chain segment:
# Extract seq 100-200 and verify hash chain
jq 'select(.seq_no >= 100 and .seq_no <= 200)' audit.jsonl > segment.jsonl
geode audit verify --file segment.jsonl --algo sha256
Integration
Syslog Integration (RFC 5424)
CEF Format (Common Event Format):
<134>1 2026-01-24T10:30:15.123Z prod-db-01 geode 1234 auth.login -
CEF:0|DEVNW|GEODE|0.1.3|auth.login|User Login|5|
act=authenticate [email protected] outcome=success
rt=Jan 24 2026 10:30:15 src=192.168.1.100
Field Mapping:
<134>: Priority (local4.info)1: RFC 5424 versionprod-db-01: Hostnamegeode: Application name1234: Process IDauth.login: Message ID- CEF fields: actor, action, outcome, timestamp, source IP
Testing Syslog:
# Test UDP syslog
nc -u -l 514
# In another terminal
echo "RETURN 1" | geode query --user alice --password test -
# Check nc output for CEF messages
SIEM Integration
Splunk:
sourcetype = geode:audit
| stats count by event_type, result
| where result="failure"
Elasticsearch:
GET /geode-audit-*/_search
{
"query": {
"bool": {
"must": [
{"term": {"event_type": "auth.login"}},
{"term": {"result": "failure"}}
]
}
}
}
Grafana Loki:
{job="geode-audit"} |= "auth.login" |= "failure" | json
Compliance
SOX (Sarbanes-Oxley)
Requirements:
- ✅ Tamper-evident audit trail (hash chain + signatures)
- ✅ Immutable log storage (append-only, 0600 permissions)
- ✅ Access tracking (all database operations logged)
- ✅ Retention policy (configurable backup_count)
- ✅ Periodic review (verification tools provided)
Configuration:
{
"sinks": {
"file": {
"backup_count": 90,
"max_bytes": 104857600
}
}
}
PCI-DSS (Payment Card Industry)
Requirements:
- ✅ Log all access to cardholder data (db.query events)
- ✅ Secure log storage (encryption at rest with TDE)
- ✅ Daily log review (automated verification)
- ✅ Retain logs for 1 year (90+ rotated files)
- ✅ Credit card redaction (Luhn algorithm detection)
Configuration:
{
"filters": {
"pci_mode": true,
"deny_key_patterns": ["card", "cvv", "pan"]
}
}
HIPAA (Health Insurance Portability)
Requirements:
- ✅ Audit controls (comprehensive event logging)
- ✅ Access tracking (actor, resource, action)
- ✅ Integrity controls (hash chain, signatures)
- ✅ PHI redaction (PII mode enabled)
- ✅ 6-year retention (adjust backup_count accordingly)
Configuration:
{
"filters": {
"pii_mode": "redact",
"deny_key_patterns": ["ssn", "dob", "mrn", "diagnosis"]
},
"sinks": {
"file": {
"backup_count": 2190
}
}
}
GDPR (General Data Protection Regulation)
Requirements:
- ✅ Data minimization (default-deny filtering)
- ✅ Right to erasure (redaction tools)
- ✅ Breach notification (alerting on access_denied)
- ✅ Pseudonymization (PII masking)
- ✅ Audit trail (all processing logged)
Configuration:
{
"filters": {
"default_deny": true,
"pii_mode": "mask",
"allow_fields": ["trace_id", "action", "outcome"]
}
}
Performance
Benchmarks
Append Latency:
- Non-blocking: <500μs (queue insertion)
- With fsync: <5ms (durable write)
- Hash computation: <100μs (SHA-256)
- Signature generation: <1ms (Ed25519, every 100 events)
Throughput:
- Single-threaded: 10,000 events/sec
- Multi-threaded: 50,000 events/sec
- With syslog: 8,000 events/sec (network overhead)
Memory Usage:
- Base: 10 MB (buffer queues)
- Per event: 1 KB (average)
- Max RSS: 512 MB (limits enforced)
Optimization
Tuning for High Volumes:
{
"buffer_size": 10000,
"batch_flush_interval_ms": 100,
"sinks": {
"file": {
"buffer_size_bytes": 1048576
}
}
}
Async Syslog (non-blocking network I/O):
{
"sinks": {
"syslog": {
"async": true,
"queue_size": 10000,
"timeout_ms": 5000
}
}
}
Troubleshooting
Common Issues
Issue: Audit log not created
Solution:
# Check directory permissions
ls -ld /var/log/geode/
# Should be drwx------ geode geode
# Create directory if missing
sudo mkdir -p /var/log/geode
sudo chown geode:geode /var/log/geode
sudo chmod 700 /var/log/geode
# Verify configuration
cat config/logging.json | jq '.sinks.file'
Issue: Verification fails with SCHEMA_ERROR
Solution:
# Check for truncated JSON lines
tail -n 1 audit.jsonl
# Remove partial line if present
head -n -1 audit.jsonl > audit_fixed.jsonl
mv audit_fixed.jsonl audit.jsonl
# Re-run verification
geode audit verify --file audit.jsonl
Issue: Syslog messages not reaching server
Solution:
# Test network connectivity
nc -zv syslog.example.com 514
# Check firewall rules
sudo iptables -L | grep 514
# Enable debug logging
GEODE_LOG_LEVEL=debug geode serve
# Verify CEF format
tcpdump -i any port 514 -A
Issue: High memory usage
Solution:
# Monitor memory
top -p $(pgrep geode)
# Reduce buffer size
jq '.buffer_size = 1000' config/logging.json > config/logging_new.json
# Enable aggressive rotation
jq '.sinks.file.max_bytes = 10485760' config/logging.json > config/logging_new.json
# Restart with new config
sudo systemctl restart geode
Best Practices
Deployment Checklist
Production deployment recommendations:
- Generate unique Ed25519 key pair (not test keys!)
- Store private key in secure vault (HashiCorp Vault, AWS Secrets Manager)
- Set
GEODE_AUDIT_PUBandGEODE_AUDIT_SECenvironment variables - Configure log rotation (90-2190 files for compliance)
- Set up remote syslog server (redundant, geographically distributed)
- Enable TDE for audit log encryption at rest
- Configure automated verification (daily cron job)
- Set up alerting for verification failures
- Document key rotation procedures
- Test disaster recovery (restore from backups)
Security Hardening
Restrict Access:
# Only geode user can read audit logs
chmod 600 /var/log/geode/audit.jsonl*
# Only root can rotate
chmod 700 /usr/local/bin/rotate_audit_logs.sh
# SELinux mandatory access control
semanage fcontext -a -t geode_log_t '/var/log/geode(/.*)?'
restorecon -Rv /var/log/geode
Network Security:
# Use TLS for syslog (port 6514)
{
"sinks": {
"syslog": {
"protocol": "tls",
"port": 6514,
"tls": {
"ca_cert": "/etc/geode/syslog-ca.pem",
"verify": true
}
}
}
}
Monitoring:
# Alert on verification failures
0 2 * * * /usr/local/bin/verify_audit_logs.sh || mail -s "Audit verification failed" [email protected]
# Monitor for tampering attempts
journalctl -u geode -f | grep TAMPER | mail -s "ALERT: Audit tampering detected" [email protected]
Code Examples
Basic Usage
const std = @import("std");
const audit = @import("audit/logger.zig");
const config = @import("audit/config.zig");
const events = @import("audit/events.zig");
const time = @import("audit/time.zig");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Load configuration
const audit_config = try config.loadConfig(allocator, "config/logging.json");
defer audit_config.deinit();
// Initialize logger
var sys_clock = time.SystemClock{};
var sys_id_gen = time.SystemIdGen.init();
const logger = try audit.AuditLogger.init(
allocator,
audit_config,
sys_clock.clock(),
sys_id_gen.idGen(),
);
defer logger.deinit();
// Emit authentication event
const auth_event = events.AuditEvent{
.category = "auth",
.action = "login",
.outcome = .success,
.ts_unix_ms = 0, // Auto-filled
.severity = .INFO,
.actor_id = "[email protected]",
.subject_id = null,
.request_id = "req-12345",
.session_id = "ses-abcdef",
.source_ip = "192.168.1.100",
.trace_id = null, // Auto-generated
.span_id = null, // Auto-generated
.metadata = null,
};
try logger.audit(&auth_event);
}
Custom Event Types
// Create custom database event
const db_event = events.AuditEvent{
.category = "db",
.action = "execute",
.outcome = .success,
.severity = .INFO,
.actor_id = "[email protected]",
.resource = "graph:main",
.metadata = .{
.db_operation = "MATCH",
.db_rowCount = 42,
.duration_ms = 15,
},
};
try logger.audit(&db_event);
References
Standards
RFC 5424: The Syslog Protocol
Common Event Format (CEF)
- ArcSight CEF specification for SIEM integration
W3C Trace Context
Ed25519 Digital Signatures
Compliance Resources
- SOX Compliance: IT audit controls
- PCI-DSS: Requirement 10 (logging and monitoring)
- HIPAA: 164.312(b) audit controls
- GDPR: Article 30 (records of processing)
Code Location
- Implementation:
src/audit/logger.zig - Configuration:
src/audit/config.zig - Tests:
tests/test_audit_logging.zig - Documentation:
docs/AUDIT_LOGGING.md
Next Steps
For Operators:
- Monitoring & Observability - Complete monitoring setup
- Telemetry Advanced - Custom metrics and dashboards
- Docker Deployment - Container audit log integration
For Security Teams:
- Security Overview - Complete security architecture
- Field-Level Encryption - Additional data protection
- Session Management - Session audit integration
For Developers:
- Client Libraries - Client-side audit event generation
- Testing Strategies - Audit log testing approaches
Document Version: 1.0 Last Updated: January 24, 2026 Status: Production Ready Compliance: SOX, PCI-DSS, HIPAA, GDPR ready