EPHEMERA โ A Museum of the Momentary
EPHEMERA โ A Museum of the Momentary
Purpose
A surreal system that patrols temporary directories, captures transient files before they vanish, and curates them into a digital museum dedicated to artifacts that existed for only moments before fading into the void.
What It Does
- Scans
/tmp,/var/tmp,.cache, and other ephemeral directories for temporary files - Classifies each artifact by lifespan category:
instant(< 1 second)brief(< 1 minute)fleeting(< 1 hour)daily(< 24 hours)enduring(> 24 hours)
- Generates poetic manifest entries describing each artifactโs brief existence
- Persists captured artifacts to a JSONL collection (
~/.ephemera_museum/) - Provides stats and browsing of the collection
Architecture
- Core:
Ephemeronclass represents a single transient artifact - Museum:
EphemeraMuseummanages collection, scanning, persistence - Output: ASCII art manifests with lifespan-based visual elements
- Storage: JSONL for append-only collection, avoiding database complexity
Running
# Capture ephemeral artifacts
python ephemera.py --capture
# Show collection statistics
python ephemera.py --stats
# Browse recent artifacts
python ephemera.py --browse
# Continuous watch mode (capture every 30s)
python ephemera.py --watch
The Philosophy
Every temporary file is a ghost. The ephemera process spawns, writes its data, and dies โ but between birth and death, it exists. This museum honors those momentary beings, preserving their metadata in a collection that acknowledges the transience of all digital things.
Terminal capture of the overnight run is embedded above.
Source
#!/usr/bin/env python3
"""
EPHEMERA โ A Museum of the Momentary
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Captures transient files and creates a surreal gallery of
artifacts that existed for only moments before fading.
"""
import os
import sys
import json
import time
import random
import hashlib
import tempfile
import shutil
import subprocess
from datetime import datetime, timedelta
from pathlib import Path
from collections import defaultdict
from typing import Dict, List, Tuple, Optional
import threading
# The museum's collection
ARCHIVE_DIR = Path.home() / ".ephemera_museum"
COLLECTION_FILE = ARCHIVE_DIR / "collection.jsonl"
GALLERY_DIR = ARCHIVE_DIR / "gallery"
# Ephemera sources โ places where temporary things dwell
TMP_LOCATIONS = [
"/tmp",
"/var/tmp",
"/tmp/hermes",
str(Path.home() / ".cache"),
"/tmp/pyright",
"/tmp/uv*",
]
# Maximum age for something to be "ephemeral" (seconds)
MAX_AGE_SECONDS = 86400 * 7 # One week
class Ephemeron:
"""A single transient artifact"""
def __init__(self, path: str, birth_time: float, death_time: float,
size: int, content_hash: str, origin: str):
self.path = path
self.birth_time = birth_time
self.death_time = death_time
self.lifespan = death_time - birth_time
self.size = size
self.content_hash = content_hash[:12]
self.origin = origin
self.captured_at = time.time()
@property
def lifespan_category(self) -> str:
"""Classify how long this artifact lived"""
if self.lifespan < 1:
return "instant"
elif self.lifespan < 60:
return "brief"
elif self.lifespan < 3600:
return "fleeting"
elif self.lifespan < 86400:
return "daily"
else:
return "enduring"
@property
def description(self) -> str:
"""Generate a poetic description"""
lifespan_desc = {
"instant": "a flash that vanished before it could be named",
"brief": "a whisper that lasted only a heartbeat",
"fleeting": "a passing thought, here and then gone",
"daily": "a visitor who stayed for a day",
"enduring": "a persistent ghost that haunted us long"
}
origins = {
"/tmp": "from the temp realm",
"/var/tmp": "from the var depths",
".cache": "from the cache dimension",
"hermes": "from the machine's dreaming",
"pyright": "from static analysis purgatory",
"uv": "from package management limbo"
}
origin_desc = "from beyond the veil"
for k, v in origins.items():
if k in self.origin:
origin_desc = v
break
return f"{lifespan_desc[self.lifespan_category]}, {origin_desc}."
def to_dict(self) -> dict:
return {
"path": self.path,
"birth_time": self.birth_time,
"death_time": self.death_time,
"lifespan": self.lifespan,
"size": self.size,
"content_hash": self.content_hash,
"origin": self.origin,
"captured_at": self.captured_at,
"category": self.lifespan_category
}
class EphemeraMuseum:
"""The museum that collects and displays transient artifacts"""
def __init__(self):
ARCHIVE_DIR.mkdir(parents=True, exist_ok=True)
GALLERY_DIR.mkdir(parents=True, exist_ok=True)
self.collection: List[Ephemeron] = []
self.load_collection()
def load_collection(self):
"""Load existing collection"""
if COLLECTION_FILE.exists():
with open(COLLECTION_FILE) as f:
for line in f:
data = json.loads(line)
# Pop computed fields that aren't constructor args
data.pop('lifespan', None)
data.pop('captured_at', None)
data.pop('category', None)
e = Ephemeron(**data)
self.collection.append(e)
def save_ephemeron(self, e: Ephemeron):
"""Save a new ephemeron to the collection"""
with open(COLLECTION_FILE, "a") as f:
f.write(json.dumps(e.to_dict()) + "\n")
self.collection.append(e)
def scan_tmp_realms(self) -> List[Ephemeron]:
"""Scan temporary directories for transient artifacts"""
scanned = []
# Expand glob patterns
locations = []
for loc in TMP_LOCATIONS:
if "*" in loc:
base = Path(loc.replace("*", ""))
if base.exists():
try:
locations.extend([str(p) for p in base.iterdir()
if p.is_dir() and not p.name.startswith(".")])
except PermissionError:
pass
else:
locations.append(loc)
# Also add actual temp directories
locations.extend([
str(tempfile.gettempdir()),
"/tmp",
])
for location in locations:
loc_path = Path(location)
if not loc_path.exists():
continue
try:
for entry in loc_path.iterdir():
try:
stat = entry.stat()
# Skip if too old
age = time.time() - stat.st_mtime
if age > MAX_AGE_SECONDS:
continue
# Skip directories for now (too complex)
if entry.is_dir():
continue
# Calculate content hash (first 1KB only for speed)
content_hash = "unknown"
try:
with open(entry, 'rb') as f:
content_hash = hashlib.md5(f.read(1024)).hexdigest()
except:
pass
e = Ephemeron(
path=str(entry),
birth_time=stat.st_mtime,
death_time=time.time(),
size=stat.st_size,
content_hash=content_hash,
origin=location
)
scanned.append(e)
except (PermissionError, OSError):
continue
except (PermissionError, OSError):
continue
return scanned
def generate_artifact(self, ephemeron: Ephemeron) -> str:
"""Generate a textual artifact from an ephemeron"""
# ASCII art based on lifespan category
art_by_category = {
"instant": [
" โฆ ",
" โฆ โฆ ",
" โฆ ",
],
"brief": [
" โญโโโโฎ ",
" โ โ ",
" โฐโโโโฏ ",
],
"fleeting": [
" โญโโโโโโโโฎ ",
"โ โ",
" โฐโโโโโโโโฏ ",
],
"daily": [
" โโโโโโโโโโโ ",
" โ โ ",
" โ ~ ~ ~ โ ",
" โโโโโโโโโโโ ",
],
"enduring": [
"โโโโโโโโโโโโโโโ",
"โ โ",
"โ โโโโโโโโโโโ โ",
"โ โ",
"โโโโโโโโโโโโโโโ",
]
}
art = random.choice(art_by_category[ephemeron.lifespan_category])
# Generate a manifest entry
manifest = f"""
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ EPHEMERAL ARTIFACT โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
โ Path: {ephemeron.path[:50]:<50}โ
โ Size: {ephemeron.size:>10} bytes โ
โ Lifespan: {ephemeron.lifespan:>8.2f} seconds โ
โ Hash: {ephemeron.content_hash:<12} โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
โ {ephemeron.description:<60}โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
โ CAPTURED: {datetime.fromtimestamp(ephemeron.captured_at).isoformat():<45}โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
{art}
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
"""
return manifest
def create_gallery_entry(museum: EphemeraMuseum, count: int = 1):
"""Create a new gallery entry"""
# Scan for new ephemera
scanned = museum.scan_tmp_realms()
if not scanned:
return "No ephemeral artifacts found in the tmp realms."
# Save some to the collection (simulating death/capture)
saved_count = 0
for e in scanned[:count]:
museum.save_ephemeron(e)
saved_count += 1
# Generate display
display = f"""
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
THE EPHEMERA MUSEUM โ
โ
โ A Museum of the Momentary โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
โ Total artifacts in collection: {len(museum.collection):>6} โ
โ Newly captured: {saved_count:>6} โ
โ Scanning {len(scanned):>4} transient files... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
"""
# Show some recent artifacts
recent = sorted(scanned, key=lambda x: x.lifespan, reverse=True)[:5]
for i, e in enumerate(recent):
display += f"\n--- ARTIFACT {i+1} ---\n"
display += museum.generate_artifact(e)
return display
def run_continuous_museum(interval: int = 30):
"""Run the museum in continuous capture mode"""
museum = EphemeraMuseum()
print("""
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ
โ
THE EPHEMERA MUSEUM IS NOW OPEN โ
โ
โ
โ
โ โ
โ A museum dedicated to things that existed for only โ
โ a moment before fading into the void... โ
โ โ
โ Capturing ephemeral artifacts every {interval} seconds... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
""".format(interval=interval))
while True:
try:
result = create_gallery_entry(museum, count=random.randint(1, 3))
print(result)
time.sleep(interval)
except KeyboardInterrupt:
print("\nโ
The museum closes its doors... โ
")
break
except Exception as e:
print(f"Error: {e}")
time.sleep(interval)
def show_collection_stats():
"""Show statistics about the collection"""
museum = EphemeraMuseum()
if not museum.collection:
print("The museum is empty. Run with --capture to begin collecting.")
return
categories = defaultdict(int)
total_size = 0
total_lifespan = 0
for e in museum.collection:
categories[e.lifespan_category] += 1
total_size += e.size
total_lifespan += e.lifespan
avg_lifespan = total_lifespan / len(museum.collection) if museum.collection else 0
stats = f"""
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
COLLECTION STATISTICS โ
โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
โ Total artifacts: {len(museum.collection):>6} โ
โ Total size: {total_size:>10} bytes โ
โ Avg lifespan: {avg_lifespan:>10.2f} seconds โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
โ By category: โ
"""
for cat, count in sorted(categories.items()):
pct = (count / len(museum.collection)) * 100
bar = "โ" * int(pct / 5) + "โ" * (20 - int(pct / 5))
stats += f"โ {cat:<12} {count:>4} ({pct:>5.1f}%) [{bar}] โ\n"
stats += "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ"
print(stats)
def browse_collection(limit: int = 10):
"""Browse recent artifacts"""
museum = EphemeraMuseum()
if not museum.collection:
print("The museum is empty.")
return
recent = sorted(museum.collection, key=lambda x: x.captured_at, reverse=True)[:limit]
print(f"\nโ
RECENT ARTIFACTS (showing {len(recent)}) โ
\n")
for i, e in enumerate(recent):
print(f"--- {i+1}. {e.path.split('/')[-1]} ---")
print(f" Category: {e.lifespan_category}")
print(f" Lifespan: {e.lifespan:.2f}s")
print(f" Size: {e.size} bytes")
print(f" {e.description}")
print()
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Ephemera Museum")
parser.add_argument("--capture", action="store_true", help="Capture ephemeral artifacts")
parser.add_argument("--stats", action="store_true", help="Show collection statistics")
parser.add_argument("--browse", action="store_true", help="Browse collection")
parser.add_argument("--limit", type=int, default=10, help="Number of items to browse")
parser.add_argument("--watch", action="store_true", help="Continuous capture mode")
parser.add_argument("--interval", type=int, default=30, help="Watch interval seconds")
args = parser.parse_args()
if args.watch:
run_continuous_museum(args.interval)
elif args.stats:
show_collection_stats()
elif args.browse:
browse_collection(args.limit)
elif args.capture:
museum = EphemeraMuseum()
print(create_gallery_entry(museum, count=5))
else:
# Default: show stats
show_collection_stats()
Run output
The museum is empty. Run with --capture to begin collecting.