snapkitty
cryptography
python
SNAPKITTYWEST commited on
Commit
8fa497d
Β·
verified Β·
1 Parent(s): 9bf24bb

push from SNAPKITTYWEST/woz-vault

Browse files
Files changed (7) hide show
  1. LICENSE +59 -0
  2. README.md +29 -0
  3. app.js +312 -0
  4. index.html +142 -0
  5. manifest.json +18 -0
  6. styles.css +392 -0
  7. sw.js +16 -0
LICENSE ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ SOVEREIGN SOURCE LICENSE v1.0
2
+ Copyright (c) 2026 Ahmad Ali Parr -- Bel Esprit D'Accord Trust
3
+ SnapKitty Collective -- All Sovereign Rights Reserved
4
+
5
+ WORM SEALED -- Evidence or Silence
6
+
7
+ GRANT OF RIGHTS
8
+
9
+ Permission is hereby granted, free of charge, to any person
10
+ obtaining a copy of this software and associated documentation
11
+ files (the "Work"), to use, study, copy, and modify the Work
12
+ for personal, educational, and non-commercial purposes, subject
13
+ to the following conditions:
14
+
15
+ 1. ATTRIBUTION
16
+ All copies, modifications, or derivative works must carry
17
+ the following notice, visibly and without alteration:
18
+
19
+ "Built on Sovereign Source -- Bel Esprit D'Accord Trust
20
+ (c) 2026 Ahmad Ali Parr -- snapkittywest.github.io"
21
+
22
+ 2. NON-COMMERCIAL USE
23
+ Commercial use of this Work -- including but not limited to
24
+ use in products, services, training datasets, or AI model
25
+ training -- requires express written permission from
26
+ Bel Esprit D'Accord Trust.
27
+
28
+ 3. TRAINING DATA RESTRICTION
29
+ This Work and its contents may NOT be used to train, fine-tune,
30
+ evaluate, or benchmark any machine learning model, large language
31
+ model, or AI system without express written permission from
32
+ Bel Esprit D'Accord Trust. This restriction applies regardless
33
+ of whether the use is commercial or non-commercial.
34
+
35
+ 4. SHARE-ALIKE
36
+ Any derivative work distributed publicly must be released
37
+ under this same Sovereign Source License v1.0 or later,
38
+ with full attribution chain preserved.
39
+
40
+ 5. WORM INTEGRITY
41
+ Any redistribution must preserve all WORM chain seals,
42
+ cryptographic hashes, and audit trail entries present in
43
+ the original Work.
44
+
45
+ 6. NO INSTITUTIONAL CAPTURE
46
+ No government agency, corporation, non-profit organization,
47
+ or institutional body may claim ownership, exclusive licensing,
48
+ or derivative rights over this Work without express written
49
+ trust agreement with Bel Esprit D'Accord Trust.
50
+
51
+ DISCLAIMER
52
+
53
+ THIS WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
54
+ EXPRESS OR IMPLIED.
55
+
56
+ Bel Esprit D'Accord Trust
57
+ SnapKitty Collective
58
+ snapkittywest.github.io
59
+ Evidence or Silence -- 2026
README.md ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # WOZ VAULT β€” Sovereign Hardware Key Vault
2
+
3
+ ```
4
+ ╔══════════════════════════════════════╗
5
+ β•‘ β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β•‘ WOZ VAULT
6
+ β•‘ β–‘ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β–‘ β•‘ ──────────────────────────
7
+ β•‘ β–‘ β”‚ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ VAULT β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β”‚ β–‘ β•‘ TYPE Hardware Key Vault
8
+ β•‘ β–‘ β”‚ β–ˆβ–ˆ β–ˆβ–ˆ β”‚ β–‘ β•‘ Key Ceremony
9
+ β•‘ β–‘ β”‚ β–ˆβ–ˆ Ed25519 Β· SEALED β–ˆβ–ˆ β”‚ β–‘ β•‘ KEYS Ed25519 sovereign
10
+ β•‘ β–‘ β”‚ β–ˆβ–ˆ WORM LOG ACTIVE β–ˆβ–ˆ β”‚ β–‘ β•‘ LOG WORM append-only
11
+ β•‘ β–‘ β”‚ β–ˆβ–ˆ ACCESS: DENIED β–ˆβ–ˆ β”‚ β–‘ β•‘ ACCESS Sovereign only
12
+ β•‘ β–‘ β”‚ β–ˆβ–ˆ β–ˆβ–ˆ β”‚ β–‘ β•‘ WIPE Remote Β· sealed
13
+ β•‘ β–‘ β”‚ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β”‚ β–‘ β•‘
14
+ β•‘ β–‘ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β–‘ β•‘ "The Woz built
15
+ β•‘ β–‘ VAULT SEAL: Ξ©β†ΊΞ¨β†ΊΞ”β†ΊΞ›β†ΊΞ£β†ΊΞ¦β†ΊΞ± β–‘ β•‘ the machine.
16
+ β•‘ β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β•‘ We sealed it."
17
+ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
18
+ ```
19
+
20
+ Hardware key vault for sovereign infrastructure. Ed25519 keys sealed at ceremony.
21
+ WORM-logged access events. Remote wipe capability. No unauthorized extraction.
22
+ Named for Steve Wozniak β€” the architect's architect.
23
+
24
+ **Keys:** Ed25519 sovereign keypair
25
+ **Log:** WORM append-only access ledger
26
+ **Seal:** Ξ©β†ΊΞ¨β†ΊΞ”β†ΊΞ›β†ΊΞ£β†ΊΞ¦β†ΊΞ±
27
+ **License:** Sovereign Source License v1.0 Β· Β© 2026 Ahmad Ali Parr
28
+
29
+ ![](https://sovereign-analytics.snapkittywest.workers.dev/canary/woz-vault)
app.js ADDED
@@ -0,0 +1,312 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";
2
+ (() => {
3
+ const config = window.__WOZ_CONFIG__ || {};
4
+ const API_BASE = String(config.OS_URL || "").replace(/\/$/, "");
5
+
6
+ const state = {
7
+ health: null,
8
+ knownEntries: new Set(),
9
+ entrySeq: 0,
10
+ soundEnabled: false,
11
+ audioContext: null,
12
+ };
13
+
14
+ const $ = (id) => document.getElementById(id);
15
+ const el = {
16
+ agentOrb: $("agent-orb"),
17
+ agentName: $("agent-name"),
18
+ agentState: $("agent-state"),
19
+ agentUptime: $("agent-uptime"),
20
+ agentLatency: $("agent-latency"),
21
+ agentNode: $("agent-node"),
22
+ agentVersion: $("agent-version"),
23
+ agentIntegrity: $("agent-integrity"),
24
+ chainTerminal: $("chain-terminal"),
25
+ chainStatus: $("chain-status"),
26
+ chainTip: $("chain-tip"),
27
+ streamDot: $("stream-dot"),
28
+ immutableState: $("immutable-state"),
29
+ entryCount: $("entry-count"),
30
+ raceList: $("race-list"),
31
+ raceSource: $("race-source"),
32
+ vectorHealth: $("vector-health"),
33
+ vectorRate: $("vector-rate"),
34
+ vectorLastSeal: $("vector-last-seal"),
35
+ vectorApi: $("vector-api"),
36
+ soundToggle: $("sound-toggle"),
37
+ cmdHistory: $("command-history"),
38
+ magmaInput: $("magma-input"),
39
+ footerSession: $("footer-session"),
40
+ utcDate: $("utc-date"),
41
+ utcClock: $("utc-clock"),
42
+ };
43
+
44
+ function api(path) { return `${API_BASE}${path}`; }
45
+
46
+ async function request(path, options = {}, timeout = 8000) {
47
+ const ctrl = new AbortController();
48
+ const timer = setTimeout(() => ctrl.abort(), timeout);
49
+ try {
50
+ const res = await fetch(api(path), {
51
+ ...options,
52
+ signal: ctrl.signal,
53
+ headers: {
54
+ Accept: "application/json",
55
+ ...(options.body ? { "Content-Type": "application/json" } : {}),
56
+ ...options.headers,
57
+ },
58
+ });
59
+ const body = await res.json().catch(() => ({}));
60
+ if (!res.ok) throw new Error(body.message || body.error || `HTTP ${res.status}`);
61
+ return body;
62
+ } finally { clearTimeout(timer); }
63
+ }
64
+
65
+ function pick(obj, paths, fallback) {
66
+ for (const p of paths) {
67
+ const v = p.split(".").reduce((o, k) => o?.[k], obj);
68
+ if (v !== undefined && v !== null) return v;
69
+ }
70
+ return fallback;
71
+ }
72
+
73
+ function formatUptime(v) {
74
+ if (typeof v === "string") return v;
75
+ const s = Math.max(0, v > 1_000_000 ? Math.floor(v / 1000) : Math.floor(Number(v || 0)));
76
+ const h = String(Math.floor(s / 3600)).padStart(2, "0");
77
+ const m = String(Math.floor((s % 3600) / 60)).padStart(2, "0");
78
+ const r = String(s % 60).padStart(2, "0");
79
+ return `${h}:${m}:${r}`;
80
+ }
81
+
82
+ function normalizeHealth(payload, latency) {
83
+ const src = payload.data || payload;
84
+ const status = String(pick(src, ["status", "health", "state"], "online")).toLowerCase();
85
+ const online = !["offline", "down", "error", "failed", "unhealthy"].includes(status);
86
+ return {
87
+ online,
88
+ status: online ? status.toUpperCase() : "OFFLINE",
89
+ name: String(pick(src, ["agent.name", "agentName", "name"], "CIPHER")),
90
+ uptime: formatUptime(pick(src, ["uptime", "system.uptime", "agent.uptime"], 0)),
91
+ latency,
92
+ node: String(pick(src, ["node", "nodeId", "instance", "agent.node"], "OS-01")),
93
+ version: String(pick(src, ["version", "build.version", "agent.version"], "v2.2.0")),
94
+ integrity: String(pick(src, ["integrity", "worm.integrity", "chain.integrity"], online ? "VERIFIED" : "UNVERIFIED")),
95
+ agents: normalizeAgents(pick(src, ["agents", "race", "agentRace", "metrics.agents"], [])),
96
+ };
97
+ }
98
+
99
+ function normalizeAgents(raw) {
100
+ if (!Array.isArray(raw) || raw.length === 0) return [];
101
+ return raw.map(a => ({
102
+ name: String(a.name || a.agent || a.key || "AGENT").toUpperCase(),
103
+ score: Math.min(100, Math.max(0, Number(a.score ?? a.value ?? a.decisions ?? 0))),
104
+ }));
105
+ }
106
+
107
+ // ── Clock ──────────────────────────────────────────────────────────────────
108
+ function tickClock() {
109
+ const now = new Date();
110
+ if (el.utcDate) el.utcDate.textContent = now.toISOString().slice(0, 10);
111
+ if (el.utcClock) el.utcClock.textContent = now.toUTCString().slice(17, 25) + " UTC";
112
+ }
113
+ tickClock();
114
+ setInterval(tickClock, 1000);
115
+
116
+ // ── Health polling ─────────────────────────────────────────────────────────
117
+ async function pollHealth() {
118
+ const t0 = performance.now();
119
+ try {
120
+ const raw = await request("/api/health");
121
+ const latency = Math.round(performance.now() - t0);
122
+ const h = normalizeHealth(raw, latency);
123
+ applyHealth(h);
124
+ } catch {
125
+ applyHealth({ online: false, status: "OFFLINE", latency: "β€”", uptime: "--:--:--",
126
+ node: "β€”", version: "β€”", integrity: "UNREACHABLE", agents: [] });
127
+ }
128
+ }
129
+
130
+ function applyHealth(h) {
131
+ state.health = h;
132
+
133
+ if (el.agentOrb) {
134
+ el.agentOrb.className = `agent-orb ${h.online ? "online" : "offline"}`;
135
+ }
136
+ if (el.agentName) el.agentName.textContent = h.name;
137
+ if (el.agentState) {
138
+ el.agentState.textContent = h.status;
139
+ el.agentState.className = `state ${h.online ? "online" : "offline"}`;
140
+ }
141
+ if (el.agentUptime) el.agentUptime.textContent = h.uptime;
142
+ if (el.agentLatency) el.agentLatency.textContent = typeof h.latency === "number" ? `${h.latency} MS` : String(h.latency);
143
+ if (el.agentNode) el.agentNode.textContent = h.node;
144
+ if (el.agentVersion) el.agentVersion.textContent = h.version;
145
+ if (el.agentIntegrity) el.agentIntegrity.textContent = h.integrity;
146
+
147
+ // System vector
148
+ if (el.vectorHealth) el.vectorHealth.textContent = h.online ? "NOMINAL" : "DEGRADED";
149
+ if (el.vectorApi) el.vectorApi.textContent = h.online ? "ONLINE" : "OFFLINE";
150
+
151
+ // Agent race
152
+ if (h.agents.length > 0) renderRace(h.agents);
153
+ }
154
+
155
+ pollHealth();
156
+ setInterval(pollHealth, 3000);
157
+
158
+ // ── WORM chain polling ─────────────────────────────────────────────────────
159
+ let chainPollRate = 0;
160
+ async function pollChain() {
161
+ const candidates = ["/api/worm/entries", "/api/agents/history", "/api/worm/tail"];
162
+ let data = null;
163
+ for (const path of candidates) {
164
+ try { data = await request(path, {}, 5000); if (data) break; } catch {}
165
+ }
166
+ if (!data) return;
167
+
168
+ const entries = Array.isArray(data) ? data : (data.entries ?? data.results ?? []);
169
+ chainPollRate++;
170
+
171
+ if (el.streamDot) el.streamDot.className = "stream-dot live";
172
+ if (el.chainStatus) el.chainStatus.textContent = "STREAMING";
173
+
174
+ let added = 0;
175
+ for (const entry of entries.slice(-20)) {
176
+ const id = entry.id ?? entry.worm_entry_id ?? entry.decision_seal ?? JSON.stringify(entry).slice(0, 32);
177
+ if (state.knownEntries.has(id)) continue;
178
+ state.knownEntries.add(id);
179
+ state.entrySeq++;
180
+ appendChainLine(entry, id);
181
+ added++;
182
+ }
183
+
184
+ if (added > 0) {
185
+ beep(880, 0.05);
186
+ const tip = entries.at(-1);
187
+ const hash = tip?.seal ?? tip?.decision_seal ?? tip?.worm_entry_id ?? "β€”";
188
+ if (el.chainTip) el.chainTip.textContent = String(hash).slice(0, 16);
189
+ if (el.entryCount) el.entryCount.textContent = String(state.knownEntries.size);
190
+ if (el.immutableState) el.immutableState.textContent = "CONFIRMED";
191
+ if (el.vectorLastSeal) el.vectorLastSeal.textContent = String(hash).slice(0, 12) + "…";
192
+ }
193
+
194
+ const evPerMin = Math.round(chainPollRate * 2);
195
+ if (el.vectorRate) el.vectorRate.textContent = `${evPerMin}/MIN`;
196
+ }
197
+
198
+ function appendChainLine(entry, id) {
199
+ const seq = String(state.entrySeq).padStart(4, "0");
200
+ const ts = (entry.created_at ?? entry.timestamp ?? entry.ts ?? new Date().toISOString()).slice(11, 23);
201
+ const actor = entry.agent ?? entry.actor ?? entry.source ?? "SYSTEM";
202
+ const evt = entry.event_type ?? entry.decision ?? entry.type ?? "SEAL";
203
+ const hash = (entry.seal ?? entry.decision_seal ?? id ?? "").slice(0, 12);
204
+
205
+ const line = document.createElement("div");
206
+ line.className = "chain-line new-entry";
207
+ line.innerHTML = `
208
+ <span class="seq">${seq}</span>
209
+ <span class="ts">${ts}</span>
210
+ <span class="event">${actor}Β·${evt}</span>
211
+ <span class="hash">${hash}…</span>
212
+ `;
213
+ if (el.chainTerminal) {
214
+ const boot = el.chainTerminal.querySelector(".boot-line");
215
+ if (boot) boot.remove();
216
+ el.chainTerminal.appendChild(line);
217
+ el.chainTerminal.scrollTop = el.chainTerminal.scrollHeight;
218
+ }
219
+ if (state.soundEnabled) beep(440 + (state.entrySeq % 8) * 55, 0.04);
220
+ }
221
+
222
+ pollChain();
223
+ setInterval(pollChain, 5000);
224
+
225
+ // ── Agent race ─────────────────────────────────────────────────────────────
226
+ const STATIC_AGENTS = ["CIPHER","VAULT","SENTINEL","ATLAS","ORACLE","FORGE","NOVA","LOC"];
227
+
228
+ function renderRace(agents) {
229
+ if (!el.raceList) return;
230
+ const list = agents.length > 0 ? agents : STATIC_AGENTS.map((n, i) => ({ name: n, score: Math.floor(Math.random() * 60 + 20) }));
231
+ el.raceList.innerHTML = list.map(a => `
232
+ <div class="race-row">
233
+ <span class="race-name">${a.name}</span>
234
+ <div class="race-track"><div class="race-fill" style="width:${a.score}%"></div></div>
235
+ <span class="race-score">${a.score}</span>
236
+ </div>
237
+ `).join("");
238
+ if (el.raceSource) el.raceSource.textContent = agents.length > 0 ? "SOURCE: HEALTH API" : "SOURCE: STATIC";
239
+ }
240
+
241
+ renderRace([]);
242
+
243
+ // ── Sound ──────────────────────────────────────────────────────────────────
244
+ function beep(freq = 440, gain = 0.1) {
245
+ if (!state.soundEnabled) return;
246
+ try {
247
+ state.audioContext = state.audioContext ?? new AudioContext();
248
+ const o = state.audioContext.createOscillator();
249
+ const g = state.audioContext.createGain();
250
+ o.type = "square";
251
+ o.frequency.value = freq;
252
+ g.gain.value = gain;
253
+ o.connect(g);
254
+ g.connect(state.audioContext.destination);
255
+ o.start();
256
+ g.gain.exponentialRampToValueAtTime(0.001, state.audioContext.currentTime + 0.08);
257
+ o.stop(state.audioContext.currentTime + 0.08);
258
+ } catch {}
259
+ }
260
+
261
+ if (el.soundToggle) {
262
+ el.soundToggle.addEventListener("click", () => {
263
+ state.soundEnabled = !state.soundEnabled;
264
+ el.soundToggle.textContent = `[ SOUND: ${state.soundEnabled ? "ON " : "OFF"} ]`;
265
+ el.soundToggle.ariaPressed = String(state.soundEnabled);
266
+ if (state.soundEnabled) beep(660, 0.15);
267
+ });
268
+ }
269
+
270
+ // ── Magma command line ─────────────────────────────────────────────────────
271
+ function appendHistory(line, cls = "muted") {
272
+ if (!el.cmdHistory) return;
273
+ const span = document.createElement("div");
274
+ span.style.color = cls === "ok" ? "var(--green)" : cls === "err" ? "var(--danger)" : "var(--muted)";
275
+ span.textContent = line;
276
+ el.cmdHistory.appendChild(span);
277
+ el.cmdHistory.scrollTop = el.cmdHistory.scrollHeight;
278
+ }
279
+
280
+ async function sendMagma() {
281
+ if (!el.magmaInput) return;
282
+ const cmd = el.magmaInput.value.trim();
283
+ if (!cmd) return;
284
+ el.magmaInput.value = "";
285
+ appendHistory(`> ${cmd}`);
286
+ try {
287
+ const res = await request("/api/agents/ask", {
288
+ method: "POST",
289
+ body: JSON.stringify({ message: cmd, agent: "forge" }),
290
+ }, 15000);
291
+ const reply = res.reply ?? res.message ?? res.text ?? JSON.stringify(res);
292
+ appendHistory(reply, "ok");
293
+ beep(660, 0.1);
294
+ } catch (err) {
295
+ appendHistory(`ERR: ${err.message}`, "err");
296
+ }
297
+ }
298
+
299
+ window.__wozSend = sendMagma;
300
+
301
+ if (el.magmaInput) {
302
+ el.magmaInput.addEventListener("keydown", e => {
303
+ if (e.key === "Enter") { e.preventDefault(); sendMagma(); }
304
+ });
305
+ }
306
+
307
+ // ── Footer session ─────────────────────────────────────────────────────────
308
+ if (el.footerSession) {
309
+ const id = Math.random().toString(36).slice(2, 10).toUpperCase();
310
+ el.footerSession.textContent = `SESSION: ${id}`;
311
+ }
312
+ })();
index.html ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="theme-color" content="#010800" />
7
+ <meta name="apple-mobile-web-app-capable" content="yes"/>
8
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
9
+ <meta name="apple-mobile-web-app-title" content="WozVault"/>
10
+ <link rel="manifest" href="manifest.json"/>
11
+ <title>WOZ Vault // Sovereign Terminal</title>
12
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
13
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
14
+ <link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet" />
15
+ <link rel="stylesheet" href="styles.css" />
16
+ <script>window.__WOZ_CONFIG__ = { OS_URL: 'https://collectivekitty.com' };</script>
17
+ </head>
18
+ <body>
19
+ <div class="crt-shell">
20
+ <div class="scanlines" aria-hidden="true"></div>
21
+ <div class="screen-glow" aria-hidden="true"></div>
22
+
23
+ <header class="topbar">
24
+ <div class="brand">
25
+ <pre class="mark" aria-hidden="true">╔═╗
26
+ β•‘Wβ•‘
27
+ β•šβ•β•</pre>
28
+ <div>
29
+ <h1>WOZ VAULT</h1>
30
+ <p>SOVEREIGN OPERATIONS TERMINAL // BIFRΓ–ST NODE 01</p>
31
+ </div>
32
+ </div>
33
+ <div class="topbar-controls">
34
+ <button id="sound-toggle" class="terminal-button" type="button" aria-pressed="false">
35
+ [ SOUND: OFF ]
36
+ </button>
37
+ <div class="clock-block">
38
+ <span id="utc-date">0000-00-00</span>
39
+ <strong id="utc-clock">00:00:00 UTC</strong>
40
+ </div>
41
+ </div>
42
+ </header>
43
+
44
+ <main class="dashboard">
45
+
46
+ <!-- 01: Agent Status -->
47
+ <section class="panel agent-panel">
48
+ <div class="panel-title">
49
+ <span>01 // AGENT STATUS</span>
50
+ <span id="health-poll" class="dim">POLL: 3S</span>
51
+ </div>
52
+ <div class="agent-core">
53
+ <div id="agent-orb" class="agent-orb offline">
54
+ <span class="orb-ring"></span>
55
+ <span class="orb-center">Β§</span>
56
+ </div>
57
+ <div class="agent-identity">
58
+ <span class="label">PRIMARY AGENT</span>
59
+ <strong id="agent-name">CIPHER</strong>
60
+ <span id="agent-state" class="state offline">AWAITING LINK</span>
61
+ </div>
62
+ </div>
63
+ <dl class="telemetry">
64
+ <div><dt>UPTIME</dt><dd id="agent-uptime">--:--:--</dd></div>
65
+ <div><dt>LATENCY</dt><dd id="agent-latency">--- MS</dd></div>
66
+ <div><dt>NODE</dt><dd id="agent-node">UNKNOWN</dd></div>
67
+ <div><dt>VERSION</dt><dd id="agent-version">--</dd></div>
68
+ <div><dt>INTEGRITY</dt><dd id="agent-integrity">UNVERIFIED</dd></div>
69
+ </dl>
70
+ </section>
71
+
72
+ <!-- 02: Live WORM Chain Tail -->
73
+ <section class="panel chain-panel">
74
+ <div class="panel-title">
75
+ <span>02 // LIVE WORM CHAIN TAIL</span>
76
+ <span><i id="stream-dot" class="stream-dot"></i> <b id="chain-status">CONNECTING</b></span>
77
+ </div>
78
+ <div class="chain-head">
79
+ <span>SEQ</span>
80
+ <span>TIMESTAMP</span>
81
+ <span>ACTOR / EVENT</span>
82
+ <span>HASH</span>
83
+ </div>
84
+ <div id="chain-terminal" class="chain-terminal" role="log" aria-live="polite">
85
+ <div class="boot-line">[BOOT] ESTABLISHING BIFRΓ–ST AUDIT LINK...</div>
86
+ </div>
87
+ <div class="chain-footer">
88
+ <span>IMMUTABLE: <b id="immutable-state">UNKNOWN</b></span>
89
+ <span>TIP: <b id="chain-tip">----------------</b></span>
90
+ <span>ENTRIES: <b id="entry-count">0</b></span>
91
+ </div>
92
+ </section>
93
+
94
+ <!-- 03: Agent Race -->
95
+ <section class="panel race-panel">
96
+ <div class="panel-title">
97
+ <span>03 // AGENT RACE</span>
98
+ <span id="race-source" class="dim">SOURCE: HEALTH API</span>
99
+ </div>
100
+ <div id="race-list" class="race-list">
101
+ <div class="race-empty">AWAITING AGENT TELEMETRY...</div>
102
+ </div>
103
+ </section>
104
+
105
+ <!-- 04: System Vector -->
106
+ <section class="panel system-panel">
107
+ <div class="panel-title">
108
+ <span>04 // SYSTEM VECTOR</span>
109
+ <span class="dim">REAL-TIME</span>
110
+ </div>
111
+ <div class="system-grid">
112
+ <div class="sys-item"><span class="sys-label">OS HEALTH</span><span class="sys-value" id="vector-health">β€”</span></div>
113
+ <div class="sys-item"><span class="sys-label">EVENT RATE</span><span class="sys-value" id="vector-rate">β€”</span></div>
114
+ <div class="sys-item"><span class="sys-label">LAST SEAL</span><span class="sys-value" id="vector-last-seal">β€”</span></div>
115
+ <div class="sys-item"><span class="sys-label">API STATUS</span><span class="sys-value" id="vector-api">β€”</span></div>
116
+ </div>
117
+ </section>
118
+
119
+ </main>
120
+
121
+ <!-- Magma command line -->
122
+ <div id="magma-form" style="margin-top:14px">
123
+ <div id="command-history"></div>
124
+ <div class="command-line">
125
+ <span style="color:var(--green-dim)">&gt;</span>
126
+ <input id="magma-input" type="text" autocomplete="off" spellcheck="false"
127
+ placeholder="Β§FORGE:CIPHER:QUERY{msg} β€” MAGMA protocol" />
128
+ <button class="terminal-button" type="button" onclick="window.__wozSend&&window.__wozSend()">SEND</button>
129
+ </div>
130
+ </div>
131
+
132
+ <footer>
133
+ <span>WORM: APPEND-ONLY Β· IMMUTABLE Β· SOVEREIGN</span>
134
+ <span id="footer-session">SESSION: β€”</span>
135
+ <span>NODE: BBQBADDIE // RTX 5000</span>
136
+ </footer>
137
+ </div>
138
+
139
+ <script src="app.js"></script>
140
+ <script>if('serviceWorker'in navigator)navigator.serviceWorker.register('sw.js').catch(()=>{})</script>
141
+ </body>
142
+ </html>
manifest.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Woz Vault",
3
+ "short_name": "WozVault",
4
+ "description": "Sovereign operations terminal β€” BIFRΓ–ST node, WORM chain, agent telemetry",
5
+ "start_url": "/woz-vault/",
6
+ "scope": "/woz-vault/",
7
+ "display": "standalone",
8
+ "background_color": "#010800",
9
+ "theme_color": "#010800",
10
+ "icons": [
11
+ {
12
+ "src": "https://sovereign-mesh.snapkittywest.workers.dev/favicon.svg",
13
+ "sizes": "any",
14
+ "type": "image/svg+xml",
15
+ "purpose": "any maskable"
16
+ }
17
+ ]
18
+ }
styles.css ADDED
@@ -0,0 +1,392 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --green: #41ff00;
3
+ --green-dim: #1a6600;
4
+ --green-glow: rgba(65, 255, 0, 0.25);
5
+ --black: #010800;
6
+ --panel: #020d01;
7
+ --border: #1a3310;
8
+ --muted: #3a6630;
9
+ --text: #41ff00;
10
+ --warn: #ffcc00;
11
+ --danger: #ff3366;
12
+ --font: "VT323", monospace;
13
+ }
14
+
15
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
16
+
17
+ html, body {
18
+ height: 100%;
19
+ background: var(--black);
20
+ color: var(--green);
21
+ font-family: var(--font);
22
+ font-size: 20px;
23
+ line-height: 1.4;
24
+ overflow-x: hidden;
25
+ }
26
+
27
+ /* ── CRT shell ─────────────────────────────────────────────────────────────── */
28
+ .crt-shell {
29
+ position: relative;
30
+ min-height: 100vh;
31
+ padding: 16px;
32
+ overflow: hidden;
33
+ animation: crt-flicker 8s infinite;
34
+ }
35
+
36
+ .scanlines {
37
+ position: fixed; inset: 0; z-index: 100; pointer-events: none;
38
+ background: repeating-linear-gradient(
39
+ to bottom,
40
+ transparent 0,
41
+ transparent 2px,
42
+ rgba(0,0,0,0.18) 2px,
43
+ rgba(0,0,0,0.18) 4px
44
+ );
45
+ }
46
+
47
+ .screen-glow {
48
+ position: fixed; inset: 0; z-index: 99; pointer-events: none;
49
+ background: radial-gradient(ellipse at 50% 50%, var(--green-glow) 0%, transparent 70%);
50
+ opacity: 0.4;
51
+ }
52
+
53
+ /* ── Topbar ────────────────────────────────────────────────────────────────── */
54
+ .topbar {
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: space-between;
58
+ gap: 16px;
59
+ padding-bottom: 12px;
60
+ border-bottom: 1px solid var(--border);
61
+ margin-bottom: 16px;
62
+ }
63
+
64
+ .brand { display: flex; align-items: center; gap: 14px; }
65
+
66
+ .mark {
67
+ font-size: 14px;
68
+ line-height: 1.2;
69
+ color: var(--green);
70
+ text-shadow: 0 0 8px var(--green);
71
+ }
72
+
73
+ .brand h1 {
74
+ font-size: 28px;
75
+ font-family: var(--font);
76
+ letter-spacing: 4px;
77
+ text-shadow: 0 0 12px var(--green);
78
+ margin: 0;
79
+ }
80
+
81
+ .brand p {
82
+ font-size: 12px;
83
+ color: var(--muted);
84
+ letter-spacing: 2px;
85
+ margin: 0;
86
+ }
87
+
88
+ .topbar-controls { display: flex; align-items: center; gap: 20px; }
89
+
90
+ .clock-block {
91
+ display: flex; flex-direction: column; align-items: flex-end;
92
+ font-size: 13px;
93
+ color: var(--muted);
94
+ }
95
+
96
+ .clock-block strong { font-size: 18px; color: var(--green); }
97
+
98
+ /* ── Terminal button ───────────────────────────────────────────────────────── */
99
+ .terminal-button {
100
+ background: transparent;
101
+ border: 1px solid var(--border);
102
+ color: var(--green);
103
+ font-family: var(--font);
104
+ font-size: 16px;
105
+ padding: 4px 12px;
106
+ cursor: pointer;
107
+ letter-spacing: 1px;
108
+ transition: border-color 0.15s, color 0.15s;
109
+ }
110
+ .terminal-button:hover { border-color: var(--green); color: var(--green); }
111
+ .terminal-button[aria-pressed="true"] { color: var(--black); background: var(--green); }
112
+
113
+ /* ── Dashboard grid ────────────────────────────────────────────────────────── */
114
+ .dashboard {
115
+ display: grid;
116
+ grid-template-columns: 1fr 2fr;
117
+ grid-template-rows: auto auto;
118
+ gap: 12px;
119
+ }
120
+
121
+ /* ── Panel ─────────────────────────────────────────────────────────────────── */
122
+ .panel {
123
+ background: var(--panel);
124
+ border: 1px solid var(--border);
125
+ padding: 14px;
126
+ }
127
+
128
+ .panel-title {
129
+ display: flex;
130
+ justify-content: space-between;
131
+ align-items: center;
132
+ font-size: 14px;
133
+ color: var(--muted);
134
+ border-bottom: 1px solid var(--border);
135
+ padding-bottom: 8px;
136
+ margin-bottom: 12px;
137
+ letter-spacing: 1px;
138
+ }
139
+
140
+ .dim { color: var(--muted); font-size: 13px; }
141
+
142
+ /* ── Agent panel ───────────────────────────────────────────────────────────── */
143
+ .agent-core {
144
+ display: flex;
145
+ align-items: center;
146
+ gap: 16px;
147
+ margin-bottom: 14px;
148
+ }
149
+
150
+ .agent-orb {
151
+ position: relative;
152
+ width: 64px; height: 64px;
153
+ border-radius: 50%;
154
+ display: flex; align-items: center; justify-content: center;
155
+ flex-shrink: 0;
156
+ }
157
+
158
+ .agent-orb.online { box-shadow: 0 0 24px var(--green), 0 0 48px rgba(65,255,0,0.2); animation: orb-pulse 2s ease-in-out infinite; }
159
+ .agent-orb.offline { opacity: 0.4; }
160
+
161
+ .orb-ring {
162
+ position: absolute; inset: 4px;
163
+ border-radius: 50%;
164
+ border: 2px solid currentColor;
165
+ animation: rotate 6s linear infinite;
166
+ }
167
+ .agent-orb.online .orb-ring { color: var(--green); }
168
+ .agent-orb.offline .orb-ring { color: var(--muted); }
169
+
170
+ .orb-center { font-size: 28px; position: relative; z-index: 1; }
171
+ .agent-orb.online .orb-center { text-shadow: 0 0 12px var(--green); }
172
+
173
+ .agent-identity { display: flex; flex-direction: column; gap: 3px; }
174
+ .agent-identity .label { font-size: 11px; color: var(--muted); letter-spacing: 1px; }
175
+ .agent-identity strong { font-size: 26px; letter-spacing: 3px; }
176
+
177
+ .state { font-size: 13px; letter-spacing: 2px; }
178
+ .state.online { color: var(--green); }
179
+ .state.offline { color: var(--muted); }
180
+ .state.processing { color: var(--warn); }
181
+
182
+ /* ── Telemetry ─────────────────────────────────────────────────────────────── */
183
+ .telemetry {
184
+ display: grid;
185
+ grid-template-columns: 1fr 1fr;
186
+ gap: 6px 12px;
187
+ font-size: 14px;
188
+ }
189
+
190
+ .telemetry div { display: flex; flex-direction: column; }
191
+ .telemetry dt { font-size: 10px; color: var(--muted); letter-spacing: 1px; }
192
+ .telemetry dd { color: var(--green); }
193
+
194
+ /* ── Chain panel ───────────────────────────────────────────────────────────── */
195
+ .chain-panel { grid-column: 2; grid-row: 1 / 3; }
196
+
197
+ .chain-head {
198
+ display: grid;
199
+ grid-template-columns: 40px 160px 1fr 120px;
200
+ gap: 8px;
201
+ font-size: 10px;
202
+ color: var(--muted);
203
+ letter-spacing: 1px;
204
+ padding: 0 4px 6px;
205
+ border-bottom: 1px solid var(--border);
206
+ margin-bottom: 4px;
207
+ }
208
+
209
+ .chain-terminal {
210
+ height: 420px;
211
+ overflow-y: auto;
212
+ font-size: 14px;
213
+ scrollbar-width: thin;
214
+ scrollbar-color: var(--border) var(--black);
215
+ }
216
+
217
+ .chain-line {
218
+ display: grid;
219
+ grid-template-columns: 40px 160px 1fr 120px;
220
+ gap: 8px;
221
+ padding: 3px 4px;
222
+ border-bottom: 1px solid rgba(26,51,16,0.4);
223
+ cursor: default;
224
+ }
225
+
226
+ .chain-line:hover { background: rgba(65,255,0,0.04); }
227
+
228
+ .chain-line .seq { color: var(--muted); }
229
+ .chain-line .ts { color: var(--muted); }
230
+ .chain-line .event { color: var(--green); }
231
+ .chain-line .hash { color: var(--green-dim); font-size: 12px; }
232
+ .chain-line.new-entry { animation: entry-flash 0.6s ease-out; }
233
+
234
+ .boot-line { color: var(--muted); padding: 4px; font-size: 14px; }
235
+
236
+ .chain-footer {
237
+ display: flex;
238
+ justify-content: space-between;
239
+ font-size: 12px;
240
+ color: var(--muted);
241
+ padding-top: 8px;
242
+ border-top: 1px solid var(--border);
243
+ margin-top: 6px;
244
+ }
245
+
246
+ .stream-dot {
247
+ display: inline-block;
248
+ width: 8px; height: 8px;
249
+ border-radius: 50%;
250
+ background: var(--muted);
251
+ vertical-align: middle;
252
+ }
253
+ .stream-dot.live { background: var(--green); box-shadow: 0 0 6px var(--green); animation: orb-pulse 1.5s infinite; }
254
+
255
+ /* ── Race panel ────────────────────────────────────────────────────────────── */
256
+ .race-list { display: flex; flex-direction: column; gap: 8px; }
257
+
258
+ .race-row {
259
+ display: grid;
260
+ grid-template-columns: 80px 1fr 60px;
261
+ align-items: center;
262
+ gap: 10px;
263
+ font-size: 15px;
264
+ }
265
+
266
+ .race-name { color: var(--green); letter-spacing: 1px; }
267
+
268
+ .race-track {
269
+ height: 8px;
270
+ background: var(--border);
271
+ border-radius: 2px;
272
+ overflow: hidden;
273
+ position: relative;
274
+ }
275
+
276
+ .race-fill {
277
+ height: 100%;
278
+ background: var(--green);
279
+ border-radius: 2px;
280
+ transition: width 0.4s ease;
281
+ box-shadow: 0 0 8px var(--green);
282
+ }
283
+
284
+ .race-score { color: var(--muted); font-size: 13px; text-align: right; }
285
+ .race-empty { color: var(--muted); font-size: 15px; }
286
+
287
+ /* ── System panel ──────────────────────────────────────────────────────────── */
288
+ .system-grid {
289
+ display: grid;
290
+ grid-template-columns: 1fr 1fr;
291
+ gap: 10px;
292
+ font-size: 15px;
293
+ }
294
+
295
+ .sys-item { display: flex; flex-direction: column; }
296
+ .sys-item .sys-label { font-size: 10px; color: var(--muted); letter-spacing: 1px; margin-bottom: 2px; }
297
+ .sys-item .sys-value { color: var(--green); }
298
+
299
+ /* ── Command line ──────────────────────────────────────────────────────────── */
300
+ .command-line {
301
+ display: flex;
302
+ align-items: center;
303
+ gap: 10px;
304
+ margin-top: 12px;
305
+ padding-top: 12px;
306
+ border-top: 1px solid var(--border);
307
+ }
308
+
309
+ #magma-input {
310
+ flex: 1;
311
+ background: transparent;
312
+ border: none;
313
+ border-bottom: 1px solid var(--border);
314
+ outline: none;
315
+ color: var(--green);
316
+ font-family: var(--font);
317
+ font-size: 18px;
318
+ padding: 2px 0;
319
+ caret-color: var(--green);
320
+ }
321
+
322
+ #command-history {
323
+ max-height: 80px;
324
+ overflow-y: auto;
325
+ font-size: 13px;
326
+ color: var(--muted);
327
+ margin-bottom: 6px;
328
+ scrollbar-width: none;
329
+ }
330
+
331
+ footer {
332
+ display: flex;
333
+ justify-content: space-between;
334
+ font-size: 13px;
335
+ color: var(--muted);
336
+ padding-top: 10px;
337
+ border-top: 1px solid var(--border);
338
+ margin-top: 12px;
339
+ letter-spacing: 1px;
340
+ }
341
+
342
+ /* ── Keyframes ─────────────────────────────────────────────────────────────── */
343
+ @keyframes crt-flicker {
344
+ 0%, 96%, 98%, 100% { opacity: 1; }
345
+ 97% { opacity: 0.93; }
346
+ 99% { opacity: 0.97; }
347
+ }
348
+
349
+ @keyframes rotate {
350
+ to { transform: rotate(360deg); }
351
+ }
352
+
353
+ @keyframes orb-pulse {
354
+ 50% { box-shadow: 0 0 36px rgba(65, 255, 0, 0.35); }
355
+ }
356
+
357
+ @keyframes vertical-scan {
358
+ from { transform: translateY(-45%); }
359
+ to { transform: translateY(45%); }
360
+ }
361
+
362
+ @keyframes entry-flash {
363
+ from { color: var(--black); background: var(--green); text-shadow: none; }
364
+ to { color: var(--green); background: transparent; }
365
+ }
366
+
367
+ /* ── Responsive ────────────────────────────────────────────────────────────── */
368
+ @media (max-width: 960px) {
369
+ .dashboard { grid-template-columns: 1fr; }
370
+ .chain-panel { grid-row: auto; }
371
+ .chain-terminal { height: 350px; }
372
+ }
373
+
374
+ @media (max-width: 650px) {
375
+ body { font-size: 18px; }
376
+ .crt-shell { padding: 8px; }
377
+ .topbar, .topbar-controls { align-items: flex-start; }
378
+ .topbar { flex-direction: column; gap: 14px; }
379
+ .topbar-controls { width: 100%; justify-content: space-between; }
380
+ .chain-head { display: none; }
381
+ .chain-line { grid-template-columns: 40px 1fr; }
382
+ .chain-line .event, .chain-line .hash { grid-column: 2; }
383
+ .chain-footer, footer { flex-wrap: wrap; gap: 6px 16px; }
384
+ .command-line button { display: none; }
385
+ }
386
+
387
+ @media (prefers-reduced-motion: reduce) {
388
+ *, *::before, *::after {
389
+ animation-duration: 0.001ms !important;
390
+ animation-iteration-count: 1 !important;
391
+ }
392
+ }
sw.js ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const CACHE = 'woz-vault-v1'
2
+ const CORE = ['/woz-vault/', '/woz-vault/index.html', '/woz-vault/app.js', '/woz-vault/styles.css']
3
+
4
+ self.addEventListener('install', e => {
5
+ self.skipWaiting()
6
+ e.waitUntil(caches.open(CACHE).then(c => c.addAll(CORE).catch(() => {})))
7
+ })
8
+
9
+ self.addEventListener('activate', e => {
10
+ e.waitUntil(caches.keys().then(keys => Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k)))))
11
+ self.clients.claim()
12
+ })
13
+
14
+ self.addEventListener('fetch', e => {
15
+ e.respondWith(caches.match(e.request).then(r => r || fetch(e.request).catch(() => caches.match('/woz-vault/'))))
16
+ })