diff --git a/hans_setup.sh b/hans_setup.sh
new file mode 100644
index 0000000..b5271c5
--- /dev/null
+++ b/hans_setup.sh
@@ -0,0 +1,117 @@
+#!/bin/bash
+set -e
+
+echo "=== Step 1: Add SSH authorized keys ==="
+mkdir -p /root/.ssh
+chmod 700 /root/.ssh
+cat > /root/.ssh/authorized_keys << 'EOF'
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICvQUpzuHN/+4xIS5dZSUY1Me7c17EhHRJdP5TkrfD39 claude@macbook
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG4TEk5EWIwLM3+/pU/H5qxZQlNUvIcxj72bYhYOZeQZ james@server
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGIhEtv7t3njNoG+mnKElR+rasMArdc8DnHON22lreT7 james@james
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK+9hJSfMkbe68VPbkRmaW/sFFmd3+QBmisJYLY+S6Cj james@forge
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN5hDM45kOB8jxk+M4Kk9in9bpwZ90sSZsPBMbzJRkbF johan@thinkpad-x1
+EOF
+chmod 600 /root/.ssh/authorized_keys
+echo "Keys written: $(wc -l < /root/.ssh/authorized_keys) keys"
+
+echo ""
+echo "=== Step 2: Harden sshd_config ==="
+cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%Y%m%d%H%M%S)
+
+apply_setting() {
+ local key="$1"
+ local value="$2"
+ if grep -qE "^#?[[:space:]]*${key}[[:space:]]" /etc/ssh/sshd_config; then
+ sed -i "s|^#\?[[:space:]]*${key}[[:space:]].*|${key} ${value}|" /etc/ssh/sshd_config
+ else
+ echo "${key} ${value}" >> /etc/ssh/sshd_config
+ fi
+}
+
+apply_setting "PermitRootLogin" "yes"
+apply_setting "PasswordAuthentication" "yes"
+apply_setting "PubkeyAuthentication" "yes"
+apply_setting "MaxAuthTries" "3"
+apply_setting "LoginGraceTime" "30"
+apply_setting "X11Forwarding" "no"
+apply_setting "AllowTcpForwarding" "no"
+apply_setting "ClientAliveInterval" "300"
+apply_setting "ClientAliveCountMax" "2"
+
+grep -q "^Protocol" /etc/ssh/sshd_config || echo "# Protocol 2 (default in OpenSSH 7+, cannot be set explicitly)" >> /etc/ssh/sshd_config
+
+sshd -t && echo "sshd config syntax OK" || echo "ERROR: sshd config syntax FAILED"
+systemctl reload ssh || systemctl reload sshd || true
+echo "sshd reloaded"
+
+echo ""
+echo "=== Step 3: Update packages ==="
+apt-get update -q
+DEBIAN_FRONTEND=noninteractive apt-get upgrade -y -q 2>&1 | tail -5
+
+echo ""
+echo "=== Step 4: Install packages ==="
+DEBIAN_FRONTEND=noninteractive apt-get install -y -q ufw fail2ban unattended-upgrades git 2>&1 | tail -5
+
+echo ""
+echo "=== Step 5: Configure UFW ==="
+ufw --force reset
+ufw default deny incoming
+ufw default allow outgoing
+ufw allow 22/tcp comment 'SSH'
+ufw allow 80/tcp comment 'HTTP'
+ufw allow 443/tcp comment 'HTTPS vault1984'
+ufw --force enable
+echo "UFW enabled"
+
+echo ""
+echo "=== Step 6: Configure fail2ban ==="
+cat > /etc/fail2ban/jail.local << 'EOF'
+[DEFAULT]
+bantime = 3600
+findtime = 600
+maxretry = 5
+
+[sshd]
+enabled = true
+port = ssh
+logpath = %(sshd_log)s
+backend = %(sshd_backend)s
+EOF
+systemctl enable fail2ban
+systemctl restart fail2ban
+echo "fail2ban configured and started"
+
+echo ""
+echo "=== Step 7: Unattended upgrades ==="
+cat > /etc/apt/apt.conf.d/20auto-upgrades << 'EOF'
+APT::Periodic::Update-Package-Lists "1";
+APT::Periodic::Unattended-Upgrade "1";
+APT::Periodic::AutocleanInterval "7";
+EOF
+echo "Unattended upgrades enabled"
+
+echo ""
+echo "=== Step 8: Timezone UTC ==="
+timedatectl set-timezone UTC
+timedatectl | grep "Time zone"
+
+echo ""
+echo "=== Verification ==="
+echo "--- UFW status ---"
+ufw status verbose
+
+echo ""
+echo "--- fail2ban status ---"
+fail2ban-client status sshd 2>/dev/null || systemctl status fail2ban --no-pager | head -10
+
+echo ""
+echo "--- SSH key fingerprints ---"
+ssh-keygen -l -f /root/.ssh/authorized_keys
+
+echo ""
+echo "--- sshd active settings ---"
+grep -E "^(PermitRootLogin|PasswordAuthentication|PubkeyAuthentication|MaxAuthTries|LoginGraceTime|X11Forwarding|AllowTcpForwarding|ClientAlive)" /etc/ssh/sshd_config
+
+echo ""
+echo "=== ALL DONE ==="
diff --git a/memory/2026-03-02.md b/memory/2026-03-02.md
new file mode 100644
index 0000000..675e0a6
--- /dev/null
+++ b/memory/2026-03-02.md
@@ -0,0 +1,174 @@
+# 2026-03-02 Daily Notes
+
+## vault1984 — Late night session (00:30–01:30 ET)
+
+### Architecture decision: Two separate Go binaries
+- **vault1984** (`/home/johan/dev/vault1984`) — pure vault server binary. Runs in each AWS region. No billing, no marketing, no website.
+- **vault1984-web** (`/home/johan/dev/vault1984-web`) — marketing website. Go binary serving static HTML from embed.FS. Runs on vault1984.com.
+- Johan was clear: "the website will run its own Go, as we need login, billing etc etc"
+- Previous session had created a mess by embedding both into the same binary.
+
+### vault1984-web: Python server killed, replaced with Go
+- Was running `python3 -m http.server 8099` (previous session left it). Johan: "python!? Are you kidding me?"
+- Replaced with Go binary (`main.go` + `//go:embed *.html *.svg *.css`)
+- Systemd service: `~/.config/systemd/user/vault1984-web.service`, port 8099
+- Caddy proxies vault1984.com → 8099
+- Committed to `git@zurich.inou.com:vault1984-web.git`
+
+### hosted.html — Global infrastructure map
+- Lives in `vault1984-web/`, served at vault1984.com/hosted.html
+- Shows all 31 real AWS commercial regions on animated world map SVG
+- Zürich highlighted in gold as HQ node
+- **Bug fixed: Beijing removed** — `cn-north-1` is China partition (separate AWS account, Sinnet-operated). NOT a commercial region.
+- **Added:** `mx-central-1` (Mexico City, 2023) and `ap-southeast-5` (Malaysia/KL, 2024) — both real commercial regions
+- The detailed world map SVG lives in the original `vault1984-web/hosted.html` — do NOT replace with simplified blob paths
+
+### vault1984 binary — partial mess still present
+- Previous session added `cmd/vault1984/website/` (copy of marketing HTML) and updated routes.go to serve both
+- This needs to be reverted — `vault1984` should be pure vault server, no marketing HTML
+- TODO: revert routes.go and main.go changes, delete cmd/vault1984/website/
+
+### AWS region knowledge
+- 31 commercial regions as of early 2026 (not 30)
+- China regions (`cn-north-1` Beijing, `cn-northwest-1` Ningxia) are a SEPARATE partition — not deployable with normal AWS account
+- GovCloud regions also excluded from vault1984 deployment
+- Newest regions: `mx-central-1` (Mexico, 2023), `ap-southeast-5` (Malaysia, 2024)
+- GCP ARM doesn't go below t2a-standard-1 (1 vCPU, 4GB RAM) — AWS Graviton unique in offering ARM at nano size (0.5GB)
+
+## vault1984-web map work (01:30–02:20 ET)
+
+### Two hosted.html files — don't confuse them
+- **vault1984-web/hosted.html** — what vault1984.com actually serves (via port 8099). Old "Hostkey" era text. Has worldmap.svg. THIS is the file we edit.
+- **vault1984/website/hosted.html** and **vault1984/cmd/vault1984/website/hosted.html** — dead copies with "Everywhere you are" / "30 AWS regions" text. NOT served. Ignore them.
+- Earlier screenshots showed the cached "30 AWS regions" version from the headless browser — that was stale cache, not live content.
+
+### worldmap.svg architecture
+- Geography SVG paths (133KB) extracted to `/home/johan/dev/vault1984-web/worldmap.svg`
+- Loaded via `` inside the SVG element
+- Dots (circles) remain inline in hosted.html
+- `fetch+DOMParser+importNode` approach failed in real Chrome — `` works everywhere
+
+### Final region selection (21 regions, Johan-approved)
+Removed from initial 31: Ohio, Oregon, Calgary, Ireland, Milan, Bahrain, Hyderabad, Melbourne, Osaka, Tokyo
+**Kept:**
+- Americas: Virginia, N.California, Montreal, Mexico City, São Paulo
+- Europe: London, Paris, Frankfurt, Zürich (HQ/gold), Spain, Stockholm
+- Middle East/Africa: UAE, Tel Aviv, Cape Town
+- Asia Pacific: Mumbai, Singapore, Jakarta, Malaysia, Sydney, Seoul, Hong Kong
+
+### Miller projection function (for future dot placement)
+```js
+const W=1000, H=460;
+function project(lon, lat) {
+ const latR = Math.min(Math.abs(lat),85)*Math.PI/180*(lat<0?-1:1);
+ const miller = 1.25*Math.log(Math.tan(Math.PI/4+0.4*latR));
+ const maxMiller = 1.25*Math.log(Math.tan(Math.PI/4+0.4*80*Math.PI/180));
+ return [(lon+180)/360*W, H/2-(miller/(2*maxMiller))*H];
+}
+```
+Zürich verification: project(8.5, 47.4) → (523.6, 117.6) ✓
+
+### Dot format (SVG circles with staggered animation)
+Each region = 3 circle elements: 2 pulse rings (fill=none) + 1 solid dot
+HQ (Zürich) = 4 elements: 3 circles + inner black dot
+Colors: green `#22C55E` for regions, gold `#D4AF37` for HQ
+
+## Hostkey VPS — "Hans" (03:56 ET)
+- **Provider:** Hostkey
+- **Location:** Switzerland (Zürich region)
+- **Purpose:** vault1984 Istanbul node (Turkey VPS via Hostkey, who confirmed they have Istanbul)
+- **Name:** Hans
+- **IP:** 185.218.204.47
+- **Hostname:** vault1984 NOC (Hostkey label)
+- **Root password:** ThIsNeEdStOcHaNgE0--
+- **Specs:** vm.mini — 4 vCPU / 6GB RAM / 120GB SSD, €3.90/mo
+- **OS:** Ubuntu 24.04
+- **Note:** Johan is ordering this live right now. Change the password first thing after provisioning.
+
+## vault1984 Styleguide (04:14 ET)
+- **vault1984.css** is the single global stylesheet for ALL vault1984 web surfaces (marketing + app)
+- Live at: https://vault1984.com/styleguide.html
+- Source: `/home/johan/dev/vault1984-web/vault1984.css`
+- Rules: one stylesheet, one width (--width:1280px via .container), one padding (--pad:2rem), CSS variables for everything, one rule per class
+- Applicable to vault1984 app UI too — copy/symlink vault1984.css into vault1984/web/
+
+## vault1984 — Beyond Passwords (04:44 ET)
+**Key insight:** vault1984 is not a password manager with notes. It's a structured knowledge store for human+AI collaboration.
+
+- Sealed fields = human-only (physical presence required): passwords, private notes, secrets
+- Agent fields = AI-accessible (scoped tokens): anything James/Claude needs to read or act on
+
+**Concrete uses:**
+- James's working memory (project plans, rollout strategies, context)
+- Shared reference data (infra details, credentials James needs)
+- Any structured note an AI agent should be able to retrieve
+- Not limited to passwords — limited only by what you want sealed vs agent-accessible
+
+**Positioning implication:**
+Proton Pass = secure notes for humans. vault1984 = knowledge store for human+AI teams.
+This is a real differentiator — needs to surface in marketing copy.
+
+## vault1984 — Storage & Pricing Implications (04:45 ET)
+- Expanding to arbitrary attachments (passport scans, MRIs, tax docs) makes storage a real cost
+- Current: $12/yr flat — works for passwords, breaks for files
+- Needs: base storage allowance (e.g. 100MB) + tiered overage, OR storage-based tiers
+- DICOM files alone can be 50MB+ — one power user could cost more than their subscription
+- Decision pending: pricing model redesign before launching file attachments
+
+## vault1984 — Text-only, Markdown default (04:47 ET)
+- No attachment/image support — ever. Text only.
+- Default format: Markdown
+- Passport number etc = just type it in. No OCR, no uploads.
+
+## vault1984 — The Memory/Encryption Tradeoff (04:48 ET)
+- Sealed = private = AI-blind. Good for passwords. Useless as AI memory.
+- Agent = AI-readable = scoped token access. Required for AI memory use case.
+- This is a conscious design tradeoff, not a flaw — user chooses per-field.
+- "James's memory in vault1984" = agent fields by definition.
+
+## vault1984 — Search Problem (04:48 ET)
+- Encrypted fields = ciphertext in SQLite = LIKE queries useless
+- Agent fields: server holds key → can decrypt-then-search server-side (acceptable, server already has access)
+- Sealed fields: server never has key → search impossible without client-side decrypt-all (doesn't scale)
+- Options: (1) server-side decrypt+search for agent fields, (2) vector embeddings, (3) unencrypted tags + encrypted body
+- Decision pending before building search
+
+## vault1984 — Search Architecture Decision (04:51 ET)
+- Decrypt-all-to-search = rejected (memory footprint, plaintext exposure)
+- **Decision: vector embeddings at write time**
+ - On save: generate embedding of plaintext → store unencrypted alongside ciphertext
+ - On search: query embeddings (no decryption) → get record IDs → decrypt only matches
+ - Cost: one embedding per write (cheap). Search: fast, minimal memory, no bulk decrypt
+- Sealed fields = unsearchable by design. User knows where they put it.
+- Agent fields = searchable via embeddings
+
+## Hans — OpenClaw + Discord Comms Channel (05:03 ET)
+**Task queued for sleeping block**
+
+### Goal
+Set up a James↔Hans communication channel so I can send deploy commands and Hans reports back.
+
+### Stack
+- **Discord server**: private, owned by us — create it
+- **Hans**: OpenClaw instance, bot connected to Discord
+- **Model**: MiniMax M2.5 via Fireworks (`accounts/fireworks/models/minimax-m2p5`)
+- **Fireworks key**: `fw_RVcDe4c6mN4utKLsgA7hTm`
+- **No Anthropic tokens on Hans** — Fireworks only
+
+### Hans's job description
+- Receive deploy commands from James via Discord
+- Execute them (pull new version, restart service)
+- Report back status
+- Simple, focused — not a general assistant
+
+### Steps
+1. Create private Discord server
+2. Create Discord bot for Hans
+3. Install OpenClaw on Hans (185.218.204.47)
+4. Configure with Fireworks M2.5, Discord bot token
+5. Test: James sends message → Hans executes → Hans replies
+
+## @vault1984 on X — Available (05:19 ET)
+- Handle @vault1984 does NOT exist — available for registration
+- Only references: 2017 WikiLeaks/CIA hashtag use, 2016 @music_vault1984 (also gone)
+- Action needed: Johan registers @vault1984 at x.com when ready
diff --git a/memory/claude-usage.db b/memory/claude-usage.db
index d37ef5a..aaf84ad 100644
Binary files a/memory/claude-usage.db and b/memory/claude-usage.db differ
diff --git a/memory/claude-usage.json b/memory/claude-usage.json
index e345946..ab2a57b 100644
--- a/memory/claude-usage.json
+++ b/memory/claude-usage.json
@@ -1,9 +1,9 @@
{
- "last_updated": "2026-03-02T05:00:01.928433Z",
+ "last_updated": "2026-03-02T11:00:02.155882Z",
"source": "api",
- "session_percent": 7,
- "session_resets": "2026-03-02T05:59:59.877299+00:00",
- "weekly_percent": 57,
- "weekly_resets": "2026-03-06T03:00:00.877347+00:00",
- "sonnet_percent": 56
+ "session_percent": 0,
+ "session_resets": "2026-03-02T16:00:00.121624+00:00",
+ "weekly_percent": 65,
+ "weekly_resets": "2026-03-06T03:00:00.121648+00:00",
+ "sonnet_percent": 69
}
\ No newline at end of file