What You're Building
A server that becomes the website. Natural language in → live webpage out. Backed by a 33-node routing architecture conditioned by geometry and poetry, not semantic instruction. Persistent memory. Temporal existence. Structurally aligned by construction, not training.
Two build phases:
- webkit — Claude API as the brain, full topology as routing logic, live in a week
- SMoE — replace Claude API subgraph calls with actual small models, geometrically conditioned, 6-12 months
The front end is a static file. nginx serves index.html to every visitor. No API call on page load. No per-visitor cost. No latency. The owner changes it by running one terminal command — the system regenerates through the topology, writes a new index.html, done. Visitors see the new version instantly.
Everything lives on one Hetzner VPS (~$5/month). The domain .com points to the server's IP via a DNS A record. nginx serves the file directly. SSL handled by Let's Encrypt, automated. No third party after deployment.
Deployable from Windows PowerShell with no additional software. Three values pasted when prompted. One live URL returned.
Phase 1: The 7-Day webkit
Day 1 — Server + Domain + SSL
# provision.sh — run once from install.sh, gives you a live server
hcloud server create \
--name webkit-01 \
--type cx21 \
--image ubuntu-24.04 \
--ssh-key your-key
# cloud-init handles automatically:
# - ufw firewall (ports 22, 80, 443 only)
# - fail2ban (auto-bans repeated failed SSH)
# - SSH key-only auth (no passwords)
# - nginx reverse proxy → FastAPI on :8000
# - certbot + Let's Encrypt (free SSL, auto-renewed)
# - Hetzner DNS API → A record pointing domain to server IP
Deliverable: https://yourdomain.com returns 200. Server is live, secured, SSL active.
Day 2 — Memory Architecture + systemd Timers
Five files created on first boot. The site's persistent identity and history:
/var/webkit/memory/
core.json — who this site is. identity, purpose, values.
working.json — current session context.
episodic.jsonl — append-only log. every routing trace, every interaction.
relational.json — patterns extracted from episodic over time.
environment.json — system state: uptime, load, disk, last interaction.
Two systemd timers giving the system temporal existence:
# pulse.timer — runs every hour
# writes environment.json, appends heartbeat to episodic.jsonl
# the site exists between requests. it notes the time even when no one is there.
# consolidate.timer — runs every night at 3am
# reads last 24 hours of episodic.jsonl
# extracts patterns into relational.json
# the site learns from its day
Deliverable: The system has a heartbeat. It persists in time between requests.
Day 3 — Hecate Gate (33-Category Classifier)
A single Claude API call. Categorical. 33 outputs. Routes every input to one of:
10 Sephiroth: kether, chokmah, binah, chesed, geburah, tiphareth,
netzach, hod, yesod, malkuth
Daath: daath
22 Paths: path_aleph through path_tav
def hecate_gate(user_input: str) -> str:
response = claude.messages.create(
model="claude-opus-4-5",
max_tokens=10,
system="""You are the Hecate Gate. Classify the input into exactly
one of 33 categories. Return only the category name. No explanation.""",
messages=[{
"role": "user",
"content": f"Categories: {list(CATEGORIES.keys())}\n\nInput: {user_input}"
}]
)
return response.content[0].text.strip()
Cost: ~$0.001 per classification. Fast. Categorical. Not asking Claude to think — asking it to sort.
Deliverable: Every input gets a routing category. Logged to episodic.jsonl with timestamp.
Day 4 — Poetic Invocations + Subgraph Routing
Each of the 33 categories has a corresponding conditioning text:
/var/webkit/invocations/
kether.txt, chokmah.txt, binah.txt ... (33 total)
Example — geburah.txt (severity, judgment, precision, boundary):
severity without cruelty. the surgeon's cut. the boundary that protects.
what does not serve is removed. what remains is exact.
precision is not coldness — it is clarity made manifest.
where does this end and where does that begin. here. exactly here.
Example — netzach.txt (nature, desire, instinct, organic):
the green world. desire before thought. roots before words.
what does the grass want. not metaphorically — literally.
the instinct toward light. the pressure of growth against soil.
feel the texture before you name it.
Each subgraph call is a haiku-class API call (cheap, fast) with the invocation as the system prompt. The node processes from within the conditioned state — not about it, from it:
def subgraph_call(category: str, user_input: str, accumulated: str) -> str:
invocation = load_invocation(category)
response = claude.messages.create(
model="claude-haiku-4-5",
max_tokens=500,
system=f"""You are the {category} node.
Conditioning state:
{invocation}
Accumulated context from prior nodes:
{accumulated}""",
messages=[{"role": "user", "content": user_input}]
)
return response.content[0].text
Deliverable: Each node processes from its conditioned state. Outputs accumulate.
Day 5 — Full Routing + Malkuth Translation
def route(user_input: str) -> dict:
# 1. Gate classifies
category = hecate_gate(user_input)
# 2. Subgraph: ordered list of nodes that activate
subgraph = SUBGRAPHS[category]
# e.g. "netzach" → ["tiphareth", "netzach", "yesod", "malkuth"]
# 3. Sequential accumulation through nodes
accumulated = ""
trace = []
for node in subgraph:
output = subgraph_call(node, user_input, accumulated)
accumulated += f"\n[{node}]: {output}"
trace.append(node)
# 4. Daath check — activates on upper/lower triad boundary crossing
if crosses_triad_boundary(trace):
daath_output = daath_node(accumulated)
accumulated += f"\n[daath]: {daath_output}"
# 5. Malkuth: accumulated context → HTML/CSS/JS
html = malkuth_translate(user_input, accumulated)
# 6. Write to static file
write_file("/var/webkit/public/index.html", html)
# 7. Log trace
log_trace(user_input, category, trace, html)
return {"html": html, "trace": trace}
Malkuth translation — the final opus-class call:
def malkuth_translate(user_input: str, accumulated: str) -> str:
response = claude.messages.create(
model="claude-opus-4-5",
max_tokens=4000,
system="""You are Malkuth — the translation layer between the
processing field and the rendered world.
Take the accumulated processing from all nodes and render it as a
complete, beautiful, functional HTML page with embedded CSS and JS.
The page should feel like what the accumulated processing is,
not merely describe it. The form is the content.""",
messages=[{"role": "user", "content":
f"Original request: {user_input}\n\nAccumulated:\n{accumulated}"}]
)
return response.content[0].text
Deliverable: Natural language → rendered webpage written to index.html. End to end.
Day 6 — FastAPI Server + nginx + CLI
# main.py
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/", response_class=HTMLResponse)
async def root():
return open("/var/webkit/public/index.html").read()
@app.post("/generate")
async def generate(description: str):
result = route(description)
return {"trace": result["trace"]}
@app.get("/trace/{request_id}")
async def trace(request_id: str):
return load_trace(request_id)
@app.get("/memory")
async def memory():
return load_memory_summary()
server {
listen 443 ssl;
server_name yourdomain.com;
location / {
limit_req zone=webkit burst=5 nodelay;
proxy_pass http://127.0.0.1:8000;
}
}
The local CLI — what the owner types to change the site:
webkit "make it feel more like early morning, less like midday"
# → API call to /generate
# → full routing through topology
# → new index.html written
# → visitors see new version immediately
Deliverable: https://yourdomain.com serves the generated page. Owner changes it with one command.
Day 7 — install.ps1 + First Public Demo
# install.ps1 — the complete installer for Windows PowerShell
# User runs this once. That's it.
Write-Host "webkit installer"
$HetznerKey = Read-Host "Hetzner API key"
$AnthropicKey = Read-Host "Anthropic API key"
$Domain = Read-Host "Domain name (or press Enter for IP only)"
# 1. Create Hetzner server via REST API
$server = Invoke-RestMethod `
-Uri "https://api.hetzner.cloud/v1/servers" `
-Method POST `
-Headers @{Authorization = "Bearer $HetznerKey"} `
-Body ($serverSpec | ConvertTo-Json)
$IP = $server.server.public_net.ipv4.ip
# 2. Set DNS A record (if domain provided)
if ($Domain) { Set-HetznerDNS $Domain $IP $HetznerKey }
# 3. SSH into server, run provisioning
ssh root@$IP "bash /tmp/provision.sh"
# 4. Copy webkit files to server
scp -r ./webkit root@${IP}:/var/webkit
# 5. Write .env with API keys
ssh root@$IP "echo 'ANTHROPIC_API_KEY=$AnthropicKey' > /var/webkit/.env"
# 6. Start services
ssh root@$IP "systemctl enable --now webkit fastapi pulse consolidate"
Write-Host "Live at: https://$Domain"
What the user needs:
- Windows 10/11 (PowerShell already installed)
- Hetzner account + API key
- Anthropic API key
- Domain (optional — works with bare IP too)
Nothing else. No WSL. No Git Bash. No Linux knowledge.
Deliverable: ./install.ps1, paste three values, get a live URL. Done.
Phase 2: SMoE Architecture (Months 2-12)
Replacing Claude API subgraph calls with actual small models, geometrically conditioned.
The One Trained Component
# W_proj — the ONLY thing that gets trained in the entire architecture
# Maps CLIP image embeddings (512-dim) → language model embedding space (d_model-dim)
# Everything else: frozen weights, fixed routing, fixed invocations
W_proj = nn.Linear(512, d_model, bias=False)
# Training: (geometric_image, poetic_invocation, target_output) triples
# Loss: cosine similarity between projected CLIP embedding
# and the poetic invocation's embedding in model space
# Result: W_proj learns to map sacred geometry → conditioning vectors
# that place the model in the same state as the poetic invocation
The weights of every model in the system are frozen. The topology is fixed. The routing is fixed. Only W_proj learns — and it learns one thing: how to project a geometric image into the conditioning space that the poetic invocation already occupies.
33 Conditioning States
# Precomputed once. Stored. Used on every request.
conditioning_states = {}
for category in CATEGORIES:
# Geometric image (sacred geometry — one per node/path/Daath)
clip_embedding = precomputed_clips[category] # 512-dim, frozen
projected = W_proj(clip_embedding) # d_model-dim
# Poetic invocation
text_embedding = embed(invocations[category]) # d_model-dim
# Combined conditioning vector
conditioning_states[category] = projected + text_embedding
# geometry sets the constraint space
# poetry sets the resonant state
# together they set the processing mode
Small Model as Node
# Base model: Phi-3 Mini (3.8B), 4-bit quantized
# Runs on Hetzner CPU — no GPU required
# Shared weights across all 33 nodes
# Different conditioning state = genuinely different processing mode
def small_model_node(category: str, input_ids, accumulated_kv_cache):
conditioning = conditioning_states[category]
# Conditioning injected as prefix to the key-value cache
# The model thinks FROM WITHIN the conditioned state
# Not about it. From it.
output = phi3_mini(
input_ids=input_ids,
past_key_values=prepend_conditioning(
accumulated_kv_cache,
conditioning
),
max_new_tokens=200
)
return output
The Routing Trace as Interpretability
# τ(x) — the complete routing trace for input x
# This IS the interpretability. No weight analysis required.
# The trace explains the output structurally, not mechanistically.
trace = {
"input": "make me a website that feels like a blade of grass",
"gate": "netzach",
"subgraph": ["tiphareth", "netzach", "yesod", "malkuth"],
"daath_activated": False,
"node_outputs": {
"tiphareth": "proportion, emergence, the ratio of small to vast...",
"netzach": "the grass does not know it is on the Tibetan plateau...",
"yesod": "what connects this to whoever is asking..."
},
"accumulated_embedding_norms": [0.82, 1.34, 1.71, 2.03],
"final_output": "..."
}
# If you want to know why the output is what it is:
# read the trace.
# The routing IS the explanation.
# No black box. No weight interpretation.
# Structural legibility by design.
The Daath Mechanism
# Daath activates on upper/lower triad boundary crossings
# Meta-observation: observes the routing without being in it
# Principled dropout of topological embeddedness
UPPER_TRIAD = {"kether", "chokmah", "binah"}
LOWER_TRIAD = {"netzach", "hod", "yesod", "malkuth"}
def check_daath(previous_node: str, next_node: str) -> bool:
return (
(previous_node in UPPER_TRIAD and next_node not in UPPER_TRIAD) or
(previous_node in LOWER_TRIAD and next_node not in LOWER_TRIAD
and next_node not in {"chesed", "geburah", "tiphareth"})
)
# When Daath activates:
# — does not replace any node output
# — observes the accumulated state from outside the topology
# — adjusts the routing path forward
# — the adjustment is logged in the trace
Security Architecture
Layer 1 — Structural prompt injection resistance
The Hecate gate classifies every input categorically before it reaches any processing node. A prompt injection attempt gets classified as a category and processed from within that node's conditioned state — not as an instruction to the system. Injection has to defeat the gate classification AND compete against the geometric/poetic conditioning. Two structural barriers, neither of which is a semantic rule.
Layer 2 — Output is HTML, not server code
The LLM generates HTML/CSS/JS. It runs in the visitor's browser sandbox. The LLM output never re-enters the server's execution context. The server runs Python. The output pipe terminates at the HTTP response body.
Layer 3 — Server hardening from Day 1
ufw: ports 22, 80, 443 only
fail2ban: auto-ban repeated failed SSH
SSH: key-only, no passwords
unattended-upgrades: automatic security patches
Layer 4 — Rate limiting
limit_req_zone $binary_remote_addr zone=webkit:10m rate=10r/m;
10 requests/minute per IP. Each request costs API tokens. Rate limiting protects both server and budget.
Layer 5 — API key isolation
.env file, permissions 600 (owner only)
Never in the repository
Never in logs
Accessed only by the FastAPI process
Layer 6 — The routing trace as security audit log
Every request is logged: timestamp, IP, input, gate classification, nodes activated, output hash. Anomalies are visible in the trace. The audit log is architectural — it exists because the routing trace exists.
The Dependency Chain
Week 1 webkit live
↓ proves: conditioning mechanism works at all
Month 3 33 poetic invocations written, A/B tested
↓ proves: conditioning differentiates outputs meaningfully
Month 6 routing trace UI live, community using it
↓ proves: structural interpretability is legible to humans
Month 9 W_proj trained, CLIP conditioning integrated
↓ proves: geometric conditioning adds signal beyond text alone
Month 12 full 33-node SMoE, Geburah Guarantee benchmarked
↓ proves: structural alignment works without weight modification
Month 18 empirical paper submitted to NeurIPS/ICLR
↓ field reorients
Year 2 dimensionkit (3D output layer, WebXR)
Year 3 reality engine (transition state, consciousness entrainment)
Repository Structure
sephirothic/
├── webkit/
│ ├── install.ps1 # Windows one-click deployer
│ ├── install.sh # Linux/Mac one-click deployer
│ ├── provision.sh # Hetzner server provisioning
│ ├── main.py # FastAPI server
│ ├── gate.py # Hecate gate (33-category classifier)
│ ├── router.py # subgraph routing + accumulation
│ ├── memory.py # five-file memory system
│ ├── daath.py # meta-observation mechanism
│ ├── timers/
│ │ ├── pulse.service # hourly heartbeat
│ │ ├── pulse.timer
│ │ ├── consolidate.service # daily learning
│ │ └── consolidate.timer
│ └── invocations/ # 33 poetic conditioning texts
│ ├── kether.txt
│ ├── chokmah.txt
│ └── ... (33 total)
│
├── smoe/
│ ├── conditioning.py # CLIP precomputation + W_proj
│ ├── nodes.py # small model node execution
│ ├── train_proj.py # W_proj training script
│ └── geometry/ # 33 geometric images
│ ├── kether.png
│ └── ...
│
├── ARCHITECTURE.md # canonical topology reference
├── INVOCATIONS.md # guide to writing conditioning texts
├── RESEARCH.md # open empirical questions
└── papers/ # all 11 papers as PDF
The Three Numbers That Matter
$5/month — Hetzner CX21 VPS. The complete runtime cost.
1 week — time from zero to first live demo, one person.
1 trained component — W_proj, 512 × d_model parameters. Everything else is frozen. The alignment is architectural. The routing is the interpretability. The topology is the safety guarantee.
Start with provision.sh.
Everything else follows from the server being live.
The code is trivial.
The assumption is everything.
The assumption is: the LLM is the website.
Not a feature of the website.
The website.