Files
dark-lord/app/search/page.tsx
drjones e0a4a20862 Tor ops: launch UI, onion list/health scripts, systemd installer, README glow-up
- Replace fake-site footer with dark-web launch CTA; fix HubChatter inside AppProviders (client crash)
- Add /launch, DEPLOY.md, onions:list|status|health, list-onion-urls, install-systemd, onion-status
- start.sh CYBERLUX_PREPARE_ONLY; copy pass on satellite pages; Navbar launch link
- README: operator cheat sheet, phone Tor note, systemd via install-systemd.sh

Made-with: Cursor
2026-04-15 22:12:44 -07:00

218 lines
8.9 KiB
TypeScript

"use client";
import Link from "next/link";
import { FormEvent, useMemo, useState } from "react";
import {
CYBERLUX_SEARCH_INDEX_SIZE,
searchCyberluxIndex,
searchZoneLabel,
VOID_LEXICON_WORDS,
type CyberluxSearchDoc,
} from "@/lib/cyberluxSearchIndex";
const LEXICON_FOG_SAMPLE = VOID_LEXICON_WORDS.slice(0, 96);
function keywordSample(doc: CyberluxSearchDoc, limit = 20): string[] {
const seen = new Set<string>();
const out: string[] = [];
for (const w of doc.keywords.split(/\s+/)) {
const t = w.trim().toLowerCase();
if (t.length < 3 || seen.has(t)) continue;
seen.add(t);
out.push(t);
if (out.length >= limit) break;
}
return out;
}
/** v3-style hostnames — one per deployment node; paths are in-app routes. */
const ZONE_ONION_HOST: Record<CyberluxSearchDoc["zone"], string> = {
hub: "clx7hubz9vmkq2wyn4pj3lrtyx8yd6x3k8fnplwj6tor.onion",
forum: "voidagg8n3kx2wz7m4pj6lrtyx8yd5xq9kfvnplwjtor.onion",
exchange: "clxexch4kz8vm7nq2wyn9pj3lrtyx8yd2x6kfnplwj.onion",
wiki: "clxwiki3jq8vmkq2wyn4pj6lrtyx8yd7x2k9fmwnplwj.onion",
syndicate: "clxsyn7kq2wz8vm4n9pj3lrtyx8yd4x3k6fnplwjtor.onion",
account: "clxacct9vmkq2wz8n4pj6lrtyx8yd1x7k3fnplwjtor.onion",
};
function onionDisplayLine(doc: CyberluxSearchDoc): string {
const host = ZONE_ONION_HOST[doc.zone];
const p = doc.path === "/" ? "" : doc.path;
return `http://${host}${p}`;
}
export default function VoidCrawlerSearchPage() {
const [query, setQuery] = useState("");
const [submitted, setSubmitted] = useState("");
const [busy, setBusy] = useState(false);
const results = useMemo(() => {
if (!submitted.trim()) return [];
return searchCyberluxIndex(submitted);
}, [submitted]);
const onSubmit = (e: FormEvent) => {
e.preventDefault();
const q = query.trim();
if (!q) return;
setBusy(true);
window.setTimeout(() => {
setSubmitted(q);
setBusy(false);
}, 320);
};
return (
<div className="min-h-screen bg-[#030806] text-[#c8e6d0]">
<div
className="pointer-events-none fixed inset-0 z-0 opacity-[0.12]"
style={{
backgroundImage:
"repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,255,65,0.06) 3px)",
}}
/>
<header className="relative z-10 border-b border-[#00ff4122] bg-[#020504]/95 px-4 py-10 backdrop-blur-sm">
<div className="mx-auto max-w-3xl text-center">
<p className="font-mono text-[10px] uppercase tracking-[0.45em] text-[#00ff41]/55">closed corpus · internal index</p>
<h1 className="mt-3 font-mono text-4xl font-black tracking-tight text-white md:text-5xl">
VOID<span className="text-[#00ff41]">CRAWLER</span>
</h1>
<p className="mx-auto mt-4 max-w-xl font-mono text-xs leading-relaxed text-[#6b9b78]">
Crawls <strong className="text-[#9bffb8]">{CYBERLUX_SEARCH_INDEX_SIZE}</strong> signed routes on{" "}
<strong className="text-[#9bffb8]">this</strong> ring only hub, forum, exchange, wiki, syndicate shells, and account surfaces.
Foreign onions and clearnet bridges are deliberately excluded from the corpus.
</p>
<div className="mx-auto mt-8 max-w-4xl border border-[#00ff4120] bg-[#040a06]/80 px-3 py-3 text-left">
<p className="mb-2 font-mono text-[9px] uppercase tracking-[0.4em] text-[#00ff41]/40">corpus lexicon fog · sample tokens</p>
<div className="flex flex-wrap justify-center gap-1">
{LEXICON_FOG_SAMPLE.map((w) => (
<span
key={w}
className="rounded border border-[#00ff41]/12 px-1 py-0.5 font-mono text-[8px] lowercase text-[#5a8f6a]"
>
{w}
</span>
))}
</div>
</div>
</div>
</header>
<main className="relative z-10 mx-auto max-w-3xl px-4 pb-32 pt-10">
<form onSubmit={onSubmit} className="relative">
<label htmlFor="void-query" className="sr-only">
Search CyberLux sites
</label>
<input
id="void-query"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="e.g. forum barter vault checkout atlas…"
className="w-full border-2 border-[#00ff4133] bg-black/80 py-4 pl-4 pr-28 font-mono text-sm text-[#00ff41] placeholder:text-[#00ff41]/25 focus:border-[#00ff41] focus:outline-none"
/>
<button
type="submit"
disabled={busy || !query.trim()}
className="absolute right-1 top-1 bottom-1 bg-[#00ff41] px-5 font-mono text-xs font-black uppercase text-black hover:bg-[#00cc33] disabled:opacity-40"
>
{busy ? "…" : "Crawl"}
</button>
</form>
<p className="mt-4 font-mono text-[10px] uppercase tracking-widest text-[#4a6b55]">
Tip: multi-word queries match <span className="text-[#6b9b78]">all</span> tokens (AND).
</p>
<div className="mt-10 space-y-6">
{busy ? (
<p className="animate-pulse text-center font-mono text-xs uppercase tracking-[0.3em] text-[#00ff41]/45">
Walking local routes
</p>
) : null}
{!busy && submitted && (
<p className="font-mono text-xs text-[#6b9b78]">
<span className="text-[#00ff41]">{results.length}</span> hit{results.length === 1 ? "" : "s"} for{" "}
<span className="text-[#c8e6d0]">{submitted}</span>
</p>
)}
{results.map((doc) => (
<article
key={doc.path}
className="border-l-4 border-[#00ff4140] bg-[#050c08]/90 py-4 pl-5 pr-4 shadow-[inset_0_0_0_1px_rgba(0,255,65,0.06)]"
>
<div className="flex flex-wrap items-center gap-2">
<span className="rounded border border-[#00ff4133] px-2 py-0.5 font-mono text-[9px] uppercase tracking-wider text-[#7af598]">
{searchZoneLabel(doc.zone)}
</span>
</div>
<h2 className="mt-2 font-mono text-lg font-bold">
<Link href={doc.path} className="text-[#7af598] hover:text-[#b4ffcc] hover:underline">
{doc.title}
</Link>
</h2>
<p className="mt-1 break-all font-mono text-[10px] leading-relaxed text-[#00ff41]/35">
{onionDisplayLine(doc)}
</p>
<p className="mt-2 font-mono text-xs leading-relaxed text-[#8eb89a]/95">{doc.description}</p>
<div className="mt-3 flex flex-wrap gap-1">
{keywordSample(doc).map((kw) => (
<span
key={kw}
className="rounded bg-[#00ff41]/08 px-1.5 py-0.5 font-mono text-[8px] lowercase text-[#6b9b78]"
>
{kw}
</span>
))}
</div>
<p className="mt-2 font-mono text-[10px] text-[#4a6b55]">
Route:{" "}
<Link href={doc.path} className="text-[#6b9b78] hover:text-[#00ff41]">
{doc.path}
</Link>
</p>
</article>
))}
{!busy && submitted && results.length === 0 ? (
<div className="border-2 border-dashed border-[#00ff4122] py-16 text-center">
<p className="font-mono text-sm text-[#6b9b78]">No in-app routes match that query.</p>
<p className="mt-3 font-mono text-[11px] text-[#4a6b55]">
Try <button type="button" className="text-[#00ff41] underline" onClick={() => setQuery("forum")}>forum</button>,{" "}
<button type="button" className="text-[#00ff41] underline" onClick={() => setQuery("checkout")}>
checkout
</button>
, or <button type="button" className="text-[#00ff41] underline" onClick={() => setQuery("atlas")}>atlas</button>.
</p>
</div>
) : null}
</div>
<nav className="mt-16 flex flex-wrap justify-center gap-4 border-t border-[#00ff4118] pt-10 font-mono text-[11px]">
<Link href="/" className="text-[#6b9b78] hover:text-[#00ff41]">
Hub
</Link>
<Link href="/forum" className="text-[#6b9b78] hover:text-[#00ff41]">
Forum
</Link>
<Link href="/hidden-wiki" className="text-[#6b9b78] hover:text-[#00ff41]">
Wiki
</Link>
<Link href="/darknet-atlas" className="text-[#6b9b78] hover:text-[#00ff41]">
Atlas
</Link>
<Link href="/links" className="text-[#6b9b78] hover:text-[#00ff41]">
Link garden
</Link>
</nav>
<p className="mt-10 text-center font-mono text-[9px] uppercase tracking-[0.25em] text-[#00ff41]/20">
void crawler v1 · {CYBERLUX_SEARCH_INDEX_SIZE} documents · internal index only
</p>
</main>
</div>
);
}