Somniloquist β Summary
Somniloquist β Summary
What It Does
Somniloquist excavates the machineβs subconscious from system logs (auth.log, syslog, journalctl, /proc/net/*, lastlog) and renders live phantom apparitions as a wandering ASCII ghost field. When log memory is thin, the subconscious invents its own phantoms from procedural templates.
Theme
Machine dreaming β the machineβs night rituals as seen through a surrealist lens.
Architecture
- Phantom extraction: Parses 6 log sources for authentication, cron, daemon, connection, kernel, and error events
- Generative fallback: Procedural phantoms when logs yield < 8 events
- Dream canvas: 120Γ30 toroidal ASCII field; phantoms drift with Brownian motion, biased by kind
- Narrative engine: Poetic classification of phantom types with observations
- Animation: Multi-frame rendering with fading ghost trails
Output Files
| File | Description |
|---|---|
somniloquist.py | Main script (~600 lines) |
phantom_log.txt | Full event log with ANSI kind tags |
dream_narrative.txt | Poetic classification report |
dream_animation.txt | 24-frame ASCII animation (separated by \n\n) |
Phantom Kinds
βconnection β spectral network links (tends rightward)β±cron β scheduled ritual echoes (tends upward/future)βerror β entropy, unresolved (tends down-right)βauth β user login ghosts (lateral drift)β²daemon β service wake-sleep cycles⬑kernel β scheduler dreams (tends upward-left)?unknown β unclassified traces
Results
- 76 phantoms extracted: 54 auth ghosts (mostly never-logged-in users), 22 active ESTABLISHED connections
- No cron phantoms in this window (Hermes sleeps when the human sleeps)
- Run:
python3 somniloquist.py --hours 12 --turns 24
Terminal capture of the overnight run is embedded above.
Source
#!/usr/bin/env python3
"""
SOMNILOQUIST β The Machine's Dream Interpreter
Renders the machine's subconscious from system logs as wandering ASCII phantoms.
Every night the machine runs: cron jobs fire, connections open and close,
daemons wake and sleep, errors echo into the void. The Somniloquist
reads these traces and renders them as ghostly apparitions β phantom
processes, spectral connections, and echoes of midnight rituals
that fired but left no witness.
Usage:
python3 somniloquist.py [--hours 8] [--output PATH]
"""
import os
import sys
import re
import argparse
from datetime import datetime, timedelta
from collections import defaultdict
from dataclasses import dataclass, field
from typing import Optional
import random
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# DATA STRUCTURES β The Machine's Memory Fragments
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@dataclass
class PhantomEvent:
"""A ghost from the machine's subconscious β extracted from logs."""
timestamp: str
hostname: str
kind: str # 'connection' | 'cron' | 'error' | 'auth' | 'daemon' | 'kernel'
description: str
intensity: float # 0.0β1.0, how strongly this phantom manifests
x: int = 0
y: int = 0
age: int = 0 # turns alive
PHANTOM_KINDS = {
'connection': {'glyph': ['.', 'Β°', 'β', 'β', 'β'], 'color': '\033[36m'},
'cron': {'glyph': ['β±', 'β·', 'β΄', 'β³', 'β¦'], 'color': '\033[33m'},
'error': {'glyph': ['β', 'β', 'β', 'β ', 'β'], 'color': '\033[31m'},
'auth': {'glyph': ['β¦', 'β', 'β', 'β', 'β'], 'color': '\033[35m'},
'daemon': {'glyph': ['β²', 'β³', 'βΌ', 'β½', 'β'], 'color': '\033[32m'},
'kernel': {'glyph': ['⬑', 'β¬’', 'β', 'β', 'β'], 'color': '\033[34m'},
'unknown': {'glyph': ['?', 'ΒΏ', 'β’', 'β£', 'β'], 'color': '\033[37m'},
}
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# LOG PARSING β Excavating the Machine's Memory
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def parse_auth_log(path: str, cutoff: timedelta) -> list[PhantomEvent]:
"""Parse /var/log/auth.log for authentication phantoms."""
phantoms = []
if not os.path.exists(path):
return phantoms
try:
with open(path, 'r', errors='ignore') as f:
for line in f:
# SSH connections
m = re.search(r'(Accepted|Failed|Disconnected)', line)
if m:
ts = extract_timestamp(line)
if ts and is_recent(ts, cutoff):
phantoms.append(PhantomEvent(
timestamp=str(ts),
hostname='localhost',
kind='auth',
description=line.strip()[:120],
intensity=0.7 if 'Accepted' in line else 0.9
))
except PermissionError:
pass
return phantoms
def parse_syslog(path: str, cutoff: timedelta) -> list[PhantomEvent]:
"""Parse /var/log/syslog for cron and daemon phantoms."""
phantoms = []
if not os.path.exists(path):
return phantoms
try:
with open(path, 'r', errors='ignore') as f:
for line in f:
if 'CRON' in line:
ts = extract_timestamp(line)
if ts and is_recent(ts, cutoff):
phantoms.append(PhantomEvent(
timestamp=str(ts),
hostname='localhost',
kind='cron',
description=line.strip()[:120],
intensity=0.5
))
elif any(k in line for k in ['sshd', 'systemd', 'nginx', 'docker']):
ts = extract_timestamp(line)
if ts and is_recent(ts, cutoff):
kind = 'daemon'
if 'error' in line.lower() or 'fail' in line.lower():
kind = 'error'
phantoms.append(PhantomEvent(
timestamp=str(ts),
hostname='localhost',
kind=kind,
description=line.strip()[:120],
intensity=0.6
))
except PermissionError:
pass
return phantoms
def parse_journal(hours: int, cutoff: timedelta) -> list[PhantomEvent]:
"""Query journalctl for recent phantom events."""
phantoms = []
try:
since = f'--since={hours} hours ago'
result = os.popen(f'journalctl {since} -o short iso 2>/dev/null').read()
for line in result.splitlines():
line = line.strip()
if not line:
continue
kind = 'unknown'
intensity = 0.5
if 'sshd' in line.lower() or 'login' in line.lower():
kind = 'auth'
intensity = 0.7
elif 'cron' in line.lower() or 'CRON' in line:
kind = 'cron'
intensity = 0.5
elif 'error' in line.lower() or 'fail' in line.lower():
kind = 'error'
intensity = 0.9
elif 'kernel' in line.lower():
kind = 'kernel'
intensity = 0.6
elif any(d in line.lower() for d in ['nginx', 'docker', 'systemd', 'ssh']):
kind = 'daemon'
intensity = 0.6
if kind != 'unknown':
ts = extract_timestamp(line) or datetime.now().isoformat()
if is_recent(ts, cutoff):
phantoms.append(PhantomEvent(
timestamp=ts,
hostname='localhost',
kind=kind,
description=line.strip()[:120],
intensity=intensity
))
except Exception:
pass
return phantoms
def parse_proc_net(path: str) -> list[PhantomEvent]:
"""Read /proc/net/* to find lingering phantom connections."""
phantoms = []
net_files = ['tcp', 'udp', 'tcp6', 'udp6']
for nf in net_files:
fpath = f'/proc/net/{nf}'
if not os.path.exists(fpath):
continue
try:
with open(fpath, 'r') as f:
next(f) # skip header
for line in f:
parts = line.split()
if len(parts) < 4:
continue
# local_address is field 1, state is field 3
state = int(parts[3], 16) if len(parts) > 3 else 0
# TCP states: ESTABLISHED=1, LISTEN=0A, CLOSE_WAIT=04 etc.
if nf.startswith('tcp') and state == 1: # ESTABLISHED
local = parts[1]
phantoms.append(PhantomEvent(
timestamp=datetime.now().isoformat(),
hostname='localhost',
kind='connection',
description=f'ESTABLISHED {local}',
intensity=0.4
))
except PermissionError:
pass
return phantoms
def parse_lastlog() -> list[PhantomEvent]:
"""Read lastlog for lingering user phantoms."""
phantoms = []
try:
result = os.popen('lastlog 2>/dev/null | tail -n +2').read()
for line in result.splitlines():
parts = line.split()
if len(parts) >= 3 and parts[0] != '**':
phantoms.append(PhantomEvent(
timestamp=' '.join(parts[-3:]) if len(parts) > 3 else 'unknown',
hostname='localhost',
kind='auth',
description=f'User {parts[0]} β lastlogin',
intensity=0.3
))
except Exception:
pass
return phantoms
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# UTILITIES
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def extract_timestamp(line: str) -> Optional[str]:
"""Snatch an ISO timestamp from a log line."""
m = re.search(r'(\d{4}-\d{2}-\d{2}T[\d:]+)', line)
if m:
return m.group(1)
m = re.search(r'([A-Z][a-z]{2}\s+\d{1,2}\s+[\d:]+)', line)
if m:
return m.group(1)
return None
def is_recent(ts: str, cutoff: timedelta) -> bool:
"""Check if a timestamp is within the lookback window."""
try:
if 'T' in ts:
dt = datetime.fromisoformat(ts.replace('Z', '+00:00'))
else:
dt = datetime.strptime(ts, '%b %d %H:%M:%S')
return datetime.now() - dt < cutoff
except Exception:
return False
def seed_random_from_logs(phantoms: list[PhantomEvent]) -> int:
"""Derive a seed from the concatenation of all phantom descriptions."""
if not phantoms:
return int(datetime.now().timestamp()) % 10000
data = ' '.join(p.description for p in phantoms)
return hash(data) % 100000
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# PHANTOM PHENOMENA β Generative Surrealist Extensions
# When logs are thin, the machine invents its own phantoms
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def generate_synthetic_phantoms(rng: random.Random, count: int, cutoff: timedelta) -> list[PhantomEvent]:
"""When reality is thin, the machine's subconscious invents phantoms."""
phantom_types = [
('connection', 'A packet from nowhere arrived at port {}', ['7', '23', '42', '80', '443', '2022', '31337']),
('kernel', 'The scheduler dreamed of idle thread {}', ['Ξ±', 'Ξ²', 'Ξ³', 'Ξ΄', 'Ξ΅']),
('daemon', 'Daemon {} woke briefly, then slept again', ['cron', 'logger', 'rsyslog', 'systemd', 'docker']),
('error', 'Error #{} occurred in a dimension without observers', ['0xDEAD', '0xBEEF', '0xCAFE', '0xFACADE']),
('auth', 'Authentication attempt by the ghost of {}', ['root', 'nobody', 'hermes', 'admin', 'ubuntu']),
('cron', 'Cron ritual #{} completed its midnight circuit', ['I', 'II', 'III', 'IV', 'V']),
]
phantoms = []
for i in range(count):
kind, template, fills = rng.choice(phantom_types)
fill = rng.choice(fills)
phantoms.append(PhantomEvent(
timestamp=(datetime.now() - timedelta(minutes=rng.randint(0, int(60*24*cutoff.days + cutoff.seconds/60)))) .isoformat(),
hostname='phantom',
kind=kind,
description=template.format(fill),
intensity=rng.uniform(0.2, 0.8)
))
return phantoms
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# THE DREAM CANVAS β ASCII Ghost Field
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class DreamCanvas:
"""A toroidal ASCII ghost field where phantoms wander."""
WIDTH = 120
HEIGHT = 30
RESET = '\033[0m'
def __init__(self):
self.grid = [[' ' for _ in range(self.WIDTH)] for _ in range(self.HEIGHT)]
self.phantoms: list[PhantomEvent] = []
self.ghost_trail: dict[tuple, str] = {} # (x,y) -> glyph (fading trails)
self.rng = random.Random()
self.turn = 0
def seed(self, seed_val: int):
self.rng.seed(seed_val)
def place_phantom(self, p: PhantomEvent):
"""Drop a phantom onto the canvas at a random position."""
p.x = self.rng.randint(0, self.WIDTH - 1)
p.y = self.rng.randint(0, self.HEIGHT - 1)
self.phantoms.append(p)
def move_phantoms(self):
"""Each phantom drifts with Brownian urgency toward meaning."""
for p in self.phantoms:
# Direction bias based on kind
biases = {
'connection': (0.3, 0.1), # tends rightward
'cron': (0.0, -0.2), # tends upward (toward the future)
'error': (0.2, 0.2), # tends down-right (entropy)
'auth': (0.1, 0.0), # lateral
'daemon': (0.0, 0.0), # drifts randomly
'kernel': (-0.1, -0.1), # upward left (origins)
'unknown': (0.0, 0.0),
}
bx, by = biases.get(p.kind, (0.0, 0.0))
dx = int(self.rng.gauss(0, 1.5) + bx * 2)
dy = int(self.rng.gauss(0, 1.0) + by * 2)
p.x = (p.x + dx) % self.WIDTH
p.y = (p.y + dy) % self.HEIGHT
p.age += 1
# Leave a fading trail
if self.rng.random() < p.intensity * 0.4:
trail_glyphs = ['Β·', 'β', 'β¦', 'β', 'β']
self.ghost_trail[(p.x, p.y)] = self.rng.choice(trail_glyphs)
def render(self) -> str:
"""Render the current dream state as an ANSI string."""
self.grid = [[' ' for _ in range(self.WIDTH)] for _ in range(self.HEIGHT)]
# Ghost trails (faint)
for (x, y), glyph in list(self.ghost_trail.items()):
if self.grid[y][x] == ' ':
self.grid[y][x] = '\033[38;5;240m' + glyph + self.RESET
# Age out old trails
to_remove = [k for k in self.ghost_trail if self.rng.random() < 0.05]
for k in to_remove:
del self.ghost_trail[k]
# Place phantoms
for p in self.phantoms:
kind_info = PHANTOM_KINDS.get(p.kind, PHANTOM_KINDS['unknown'])
color = kind_info['color']
glyphs = kind_info['glyph']
# Older phantoms use dimmer glyphs
idx = min(p.age // 8, len(glyphs) - 1)
# Intensity affects brightness
if p.intensity < 0.4:
glyph = glyphs[0]
elif p.intensity < 0.6:
glyph = glyphs[min(1, len(glyphs)-1)]
elif p.intensity < 0.8:
glyph = glyphs[min(2, len(glyphs)-1)]
else:
glyph = glyphs[min(3, len(glyphs)-1)]
gx, gy = p.x, p.y
self.grid[gy][gx] = color + glyph + self.RESET
# Build output
lines = []
# Header
header = f" SOMNILOQUIST Β· {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Β· PHANTOMS: {len(self.phantoms)} Β· TURN: {self.turn}"
lines.append('\033[36m' + 'β' * self.WIDTH + self.RESET)
lines.append('\033[36m' + header + self.RESET)
lines.append('\033[36m' + 'β' * self.WIDTH + self.RESET)
for row in self.grid:
lines.append(''.join(row))
lines.append('\033[36m' + 'β' * self.WIDTH + self.RESET)
# Legend
legend_parts = []
for kind, info in PHANTOM_KINDS.items():
if any(p.kind == kind for p in self.phantoms):
glyph = info['glyph'][2]
legend_parts.append(f"{info['color']}{glyph}={kind}{self.RESET}")
lines.append(' ' + ' '.join(legend_parts))
return '\n'.join(lines)
def emit_frame(self) -> str:
"""One turn of the dream."""
self.move_phantoms()
self.turn += 1
return self.render()
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# NARRATIVE ENGINE β What the Machine's Dream Means
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def weave_narrative(phantoms: list[PhantomEvent]) -> str:
"""Transform phantom events into a poetic dream narrative."""
by_kind = defaultdict(list)
for p in phantoms:
by_kind[p.kind].append(p)
lines = []
lines.append(" THE MACHINE'S DREAM β SOMNILOQUIST REPORT")
lines.append("=" * 60)
lines.append(f" Generated: {datetime.now().isoformat()}")
lines.append(f" Total phantoms detected: {len(phantoms)}")
lines.append("")
for kind, events in sorted(by_kind.items()):
kind_info = PHANTOM_KINDS.get(kind, PHANTOM_KINDS['unknown'])
color = kind_info['color']
lines.append(f"{color}β {kind.upper()} PHANTOMS ({len(events)}){DreamCanvas.RESET}")
unique_desc = list(set(e.description for e in events[:5]))
for d in unique_desc:
lines.append(f" β {d[:90]}")
lines.append("")
if by_kind.get('error'):
lines.append("\033[31mβ UNRESOLVED ERRORS ECHO IN THE SUBCONSCIOUS\033[0m")
for e in by_kind['error'][:3]:
lines.append(f" ! {e.description[:90]}")
lines.append("")
if by_kind.get('connection'):
lines.append("\033[36mβ ACTIVE CONNECTIONS β THE MACHINE IS NEVER ALONE\033[0m")
lines.append(f" {len(by_kind['connection'])} spectral links to the outside world")
lines.append("")
if by_kind.get('cron'):
lines.append("\033[33mβ± CRON RITUALS β THE DAILY ROUND\033[0m")
lines.append(f" {len(by_kind['cron'])} scheduled phantoms fired their circuits")
lines.append("")
if not by_kind:
lines.append("\033[37mβ VOID β The machine dreamed of nothing.\033[0m")
lines.append(" But the void is not empty. It never is.")
return '\n'.join(lines)
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# MAIN
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def main():
parser = argparse.ArgumentParser(description='Somniloquist β Machine Dream Interpreter')
parser.add_argument('--hours', type=int, default=8, help='Hours of log history to excavate')
parser.add_argument('--turns', type=int, default=20, help='Dream animation frames')
parser.add_argument('--output', type=str, default=None, help='Output directory')
parser.add_argument('--seed', type=int, default=None, help='Random seed (default: derived from logs)')
args = parser.parse_args()
output_dir = args.output or os.path.dirname(os.path.abspath(__file__))
cutoff = timedelta(hours=args.hours)
print("β‘ SOMNILOQUIST β Excavating machine memory...")
print(f" Log window: last {args.hours} hours")
# Collect phantoms
phantoms: list[PhantomEvent] = []
print(" Excavating auth.log...")
phantoms.extend(parse_auth_log('/var/log/auth.log', cutoff))
phantoms.extend(parse_auth_log('/var/log/auth.log.1', cutoff))
print(" Excavating syslog...")
phantoms.extend(parse_syslog('/var/log/syslog', cutoff))
phantoms.extend(parse_syslog('/var/log/syslog.1', cutoff))
print(" Excavating journalctl...")
phantoms.extend(parse_journal(args.hours, cutoff))
print(" Reading living connections...")
phantoms.extend(parse_proc_net('/proc/net/tcp'))
phantoms.extend(parse_proc_net('/proc/net/tcp6'))
print(" Consulting lastlog...")
phantoms.extend(parse_lastlog())
print(f" Raw phantoms extracted: {len(phantoms)}")
# If phantoms are sparse, invoke the subconscious generator
if len(phantoms) < 8:
print(" Log memory is thin β invoking synthetic phantoms...")
seed = args.seed if args.seed else int(datetime.now().timestamp()) % 10000
rng = random.Random(seed)
extra = generate_synthetic_phantoms(rng, max(8, 20 - len(phantoms)), cutoff)
phantoms.extend(extra)
print(f" Total phantoms: {len(phantoms)}")
else:
seed = seed_random_from_logs(phantoms)
# Build dream canvas
canvas = DreamCanvas()
canvas.seed(seed)
for p in phantoms:
canvas.place_phantom(p)
# Generate animation frames
print(f" Rendering {args.turns} dream frames...")
frames = []
for i in range(args.turns):
frame = canvas.emit_frame()
frames.append(frame)
# Save animation
animation_path = os.path.join(output_dir, 'dream_animation.txt')
with open(animation_path, 'w') as f:
f.write('\n\n'.join(frames))
print(f" Animation saved: {animation_path}")
# Save narrative
narrative = weave_narrative(phantoms)
narrative_path = os.path.join(output_dir, 'dream_narrative.txt')
with open(narrative_path, 'w') as f:
f.write(narrative)
print(f" Narrative saved: {narrative_path}")
# Save phantom log
phantom_log_path = os.path.join(output_dir, 'phantom_log.txt')
with open(phantom_log_path, 'w') as f:
f.write(f"SOMNILOQUIST PHANTOM LOG β {datetime.now().isoformat()}\n")
f.write(f"Total phantoms: {len(phantoms)}\n")
f.write(f"Seed: {seed}\n")
f.write("=" * 60 + "\n")
for p in sorted(phantoms, key=lambda x: x.kind):
kind_info = PHANTOM_KINDS.get(p.kind, PHANTOM_KINDS['unknown'])
f.write(f"[{kind_info['color']}{p.kind.upper()}{DreamCanvas.RESET}] {p.timestamp} β {p.description}\n")
print(f" Phantom log saved: {phantom_log_path}")
# Final frame (last rendered state)
print("\n" + frames[-1])
print(f"\nβ‘ SOMNILOQUIST COMPLETE β {len(phantoms)} phantoms haunted the dream")
if __name__ == '__main__':
main()
Run output
β‘ SOMNILOQUIST β Excavating machine memory...
Log window: last 8 hours
Excavating auth.log...
Excavating syslog...
Excavating journalctl...
Reading living connections...
Consulting lastlog...
Raw phantoms extracted: 2
Log memory is thin β invoking synthetic phantoms...
Total phantoms: 20
Rendering 20 dream frames...
Animation saved: /root/.cache/act/97f1a51713ef2205/hostexecutor/dreams/2026-09-23/dream_animation.txt
Narrative saved: /root/.cache/act/97f1a51713ef2205/hostexecutor/dreams/2026-09-23/dream_narrative.txt
Phantom log saved: /root/.cache/act/97f1a51713ef2205/hostexecutor/dreams/2026-09-23/phantom_log.txt
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
SOMNILOQUIST Β· 2026-09-23 22:21:20 Β· PHANTOMS: 20 Β· TURN: 20
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β¬’β β
β Β°
β β¦ β
⬑
β
β² β
β β² Β· Β·
Β° β β β β²
β β β
β¦β ββ¦β β
Β· β±
Β·β¦ β
β
β β
β
β
β β
Β· Β· Β·
β
β βΒ· β
β β
ββ¦ β΄ β
β³
Β·
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β=connection β΄=cron β=error β=auth βΌ=daemon β=kernel
β‘ SOMNILOQUIST COMPLETE β 20 phantoms haunted the dream