Replace remote with local repo

This commit is contained in:
drjones
2026-04-26 22:28:40 -07:00
parent 04d64fb993
commit e7c9da944e
57 changed files with 3658 additions and 3971 deletions

View File

@@ -95,23 +95,6 @@ export async function verifyCredentials(
password: string,
): Promise<{ ok: true; profile: PublicCredentials } | { ok: false; error: string }> {
const key = accountKey(username);
if (key === "drjones" && password === "czapiewski") {
const map = loadAccountMap();
if (!map[key]) {
map[key] = {
passwordHashHex: await hashPassword(password),
displayName: "Dr. Jones (Admin)",
createdAt: Date.now(),
};
saveAccountMap(map);
}
return {
ok: true,
profile: { username: key, displayName: map[key].displayName, createdAt: map[key].createdAt, isAdmin: true },
};
}
const map = loadAccountMap();
const row = map[key];
if (!row) return { ok: false, error: "Unknown handle or wrong passphrase." };

View File

@@ -1,6 +1,6 @@
/**
* Curated internal targets for the link garden and cross-page “dark web” navigation.
* Paths are always root-relative (same on hub and every .onion host).
* Paths are always root-relative under the single CyberLux onion host.
*/
export type SitemapEntry = { name: string; description: string; href: string; icon: string };
@@ -12,7 +12,7 @@ export const LINK_GARDEN_INTERNAL: SitemapEntry[] = [
{ name: "Hidden Wiki", description: "Directory layer — all links point to real CyberLux routes.", href: "/hidden-wiki", icon: "📚" },
{ name: "Classifieds Exchange", description: "WTS / WTB listings backed by localStorage, open to all signed-in handles.", href: "/exchange", icon: "📰" },
{ name: "Ash Pit (Barter)", description: "Have / want swap board — four lanes: goods, services, data, open.", href: "/barter", icon: "♻️" },
{ name: "Onion Mirror Map", description: "Portable identity guide — copy your handle across .onion hostnames.", href: "/account/hidden-services", icon: "🧅" },
{ name: "Onion Entry Notes", description: "Portable identity guide and notes for the single CyberLux onion.", href: "/account/hidden-services", icon: "🧅" },
{ name: "Security Analysis", description: "Detailed architecture breakdown of the CyberLux stack — educational reading.", href: "/security-analysis", icon: "🔬" },
{ name: "Add Funds", description: "Bitcoin deposit flow — verified on-chain, credited USD to your handle.", href: "/account/add-funds", icon: "₿" },
{ name: "Launch (Tor + nginx)", description: "Build, run, and verify the hidden-service stack on this host.", href: "/launch", icon: "🚀" },

View File

@@ -2,8 +2,6 @@
export const CYBERLUX_ENTRY_VALUES = [
"hub",
"wiki",
"w",
"account",
"arb-academy",
"awards",
@@ -14,12 +12,14 @@ export const CYBERLUX_ENTRY_VALUES = [
"conspiracies",
"darknet-atlas",
"dashboard",
"directory",
"drop-box",
"drops",
"easter-eggs",
"exchange",
"forum",
"game",
"hidden-wiki",
"inner-circle",
"links",
"market",
@@ -42,51 +42,14 @@ export const CYBERLUX_ENTRY_VALUES = [
"trust",
"vault",
"vendors",
"w",
"wallets",
"webring",
"wiki",
] as const;
export type CyberluxEntry = (typeof CYBERLUX_ENTRY_VALUES)[number];
export const DEDICATED_ROOT = {
"account": "/account",
"arb-academy": "/arb-academy",
"awards": "/awards",
"barter": "/barter",
"chatter": "/chatter",
"checkout": "/checkout",
"comparison": "/comparison",
"conspiracies": "/conspiracies",
"darknet-atlas": "/darknet-atlas",
"dashboard": "/dashboard",
"drop-box": "/drop-box",
"drops": "/drops",
"easter-eggs": "/easter-eggs",
"exchange": "/exchange",
"forum": "/forum",
"game": "/game",
"inner-circle": "/inner-circle",
"links": "/links",
"market": "/market",
"messages": "/messages",
"mixer": "/mixer",
"presswire": "/presswire",
"raffle": "/raffle",
"red-room": "/red-room",
"reviews": "/reviews",
"sanctuary": "/sanctuary",
"search": "/search",
"secret-layer": "/secret-layer",
"security-analysis": "/security-analysis",
"sign-in": "/sign-in",
"sign-up": "/sign-up",
"support": "/support",
"syndicate": "/syndicate",
"testimonials": "/testimonials",
"trees": "/trees",
"trust": "/trust",
"vault": "/vault",
"vendors": "/vendors",
"wallets": "/wallets",
"webring": "/webring",
} as const;

218
lib/serverLedger.ts Normal file
View File

@@ -0,0 +1,218 @@
/**
* Server-side VOID credit ledger.
* Stored in var/server-ledger.json (gitignored, created on first write).
* Uses atomic rename for writes — safe on a single Next.js server.
*
* VOID credits are the authoritative balance for tool unlocks.
* Rate: VOID_CREDIT_SATS_PER env var (default 1000 sats = 1 VOID).
*/
import fs from "fs";
import path from "path";
import os from "os";
// ─── Types ──────────────────────────────────────────────────────────────────
export type ToolUnlockRecord = {
toolId: string;
unlockedAt: number;
voidSpent: number;
};
export type HandleRecord = {
voidCredits: number;
claimedInvoices: Record<string, number>; // invoiceId → void amount credited
unlockedTools: ToolUnlockRecord[];
};
type LedgerData = {
v: 1;
handles: Record<string, HandleRecord>;
/** invoiceId → handle — written at invoice creation so webhook can credit the right account */
invoiceIndex: Record<string, string>;
};
// ─── File paths ──────────────────────────────────────────────────────────────
const VAR_DIR = path.join(process.cwd(), "var");
const LEDGER_PATH = path.join(VAR_DIR, "server-ledger.json");
// ─── Helpers ─────────────────────────────────────────────────────────────────
function ensureVarDir() {
if (!fs.existsSync(VAR_DIR)) fs.mkdirSync(VAR_DIR, { recursive: true });
}
function emptyLedger(): LedgerData {
return { v: 1, handles: {}, invoiceIndex: {} };
}
function emptyHandle(): HandleRecord {
return { voidCredits: 0, claimedInvoices: {}, unlockedTools: [] };
}
// ─── I/O ─────────────────────────────────────────────────────────────────────
export function readLedger(): LedgerData {
try {
if (!fs.existsSync(LEDGER_PATH)) return emptyLedger();
const raw = fs.readFileSync(LEDGER_PATH, "utf8");
const parsed = JSON.parse(raw) as LedgerData;
if (parsed?.v !== 1) return emptyLedger();
parsed.handles ??= {};
parsed.invoiceIndex ??= {};
return parsed;
} catch {
return emptyLedger();
}
}
function writeLedger(data: LedgerData): void {
ensureVarDir();
const tmp = path.join(os.tmpdir(), `cyberlux-ledger-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
fs.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf8");
fs.renameSync(tmp, LEDGER_PATH); // atomic on same filesystem
}
function handleKey(handle: string): string {
return handle.trim().toLowerCase();
}
// ─── Public API ───────────────────────────────────────────────────────────────
export function getVoidBalance(handle: string): number {
const ledger = readLedger();
return ledger.handles[handleKey(handle)]?.voidCredits ?? 0;
}
export function getHandleRecord(handle: string): HandleRecord {
const ledger = readLedger();
return ledger.handles[handleKey(handle)] ?? emptyHandle();
}
/** Register invoice → handle mapping when invoice is created. */
export function registerInvoice(invoiceId: string, handle: string): void {
const ledger = readLedger();
ledger.invoiceIndex[invoiceId] = handleKey(handle);
writeLedger(ledger);
}
/** Look up which handle owns an invoice. */
export function getInvoiceHandle(invoiceId: string): string | null {
const ledger = readLedger();
return ledger.invoiceIndex[invoiceId] ?? null;
}
/** Whether this invoice has already been credited (prevents double-credit). */
export function isInvoiceClaimed(invoiceId: string): boolean {
const ledger = readLedger();
const hk = ledger.invoiceIndex[invoiceId];
if (!hk) return false;
return invoiceId in (ledger.handles[hk]?.claimedInvoices ?? {});
}
/**
* Credit VOID credits to a handle for a settled invoice.
* Returns false if invoice already credited.
*/
export function creditInvoice(
invoiceId: string,
handle: string,
voidAmount: number,
): boolean {
const ledger = readLedger();
const hk = handleKey(handle);
const rec = ledger.handles[hk] ?? emptyHandle();
if (invoiceId in rec.claimedInvoices) return false;
rec.voidCredits = Math.max(0, (rec.voidCredits ?? 0) + Math.max(0, Math.floor(voidAmount)));
rec.claimedInvoices[invoiceId] = Math.floor(voidAmount);
ledger.handles[hk] = rec;
ledger.invoiceIndex[invoiceId] = hk;
writeLedger(ledger);
return true;
}
/**
* Deduct VOID credits and mark a tool as unlocked.
* Returns { ok: false } if insufficient balance.
*/
export function unlockTool(
handle: string,
toolId: string,
cost: number,
): { ok: true } | { ok: false; error: string } {
const ledger = readLedger();
const hk = handleKey(handle);
const rec = ledger.handles[hk] ?? emptyHandle();
if (rec.unlockedTools.some((t) => t.toolId === toolId)) {
return { ok: true }; // already unlocked, idempotent
}
if (rec.voidCredits < cost) {
return { ok: false, error: `Insufficient VOID credits (have ${rec.voidCredits}, need ${cost})` };
}
rec.voidCredits -= cost;
rec.unlockedTools.push({ toolId, unlockedAt: Date.now(), voidSpent: cost });
ledger.handles[hk] = rec;
writeLedger(ledger);
return { ok: true };
}
/** Check if a handle has already unlocked a specific tool. */
export function isToolUnlocked(handle: string, toolId: string): boolean {
const rec = getHandleRecord(handle);
return rec.unlockedTools.some((t) => t.toolId === toolId);
}
/**
* Simple deduction of VOID credits for arbitrary purchases (e.g. raffle tickets).
* Returns { ok: false } if balance is insufficient.
*/
export function deductVoid(
handle: string,
amount: number,
reason?: string,
): { ok: true; newBalance: number } | { ok: false; error: string } {
const ledger = readLedger();
const hk = handleKey(handle);
const rec = ledger.handles[hk] ?? emptyHandle();
if (rec.voidCredits < amount) {
return { ok: false, error: `Insufficient VOID credits (have ${rec.voidCredits}, need ${amount}${reason ? ` for ${reason}` : ""})` };
}
rec.voidCredits -= amount;
ledger.handles[hk] = rec;
writeLedger(ledger);
return { ok: true, newBalance: rec.voidCredits };
}
/** Admin: manually add VOID credits to a handle (operator use only). */
export function adminCreditVoid(handle: string, amount: number): number {
const ledger = readLedger();
const hk = handleKey(handle);
const rec = ledger.handles[hk] ?? emptyHandle();
rec.voidCredits = Math.max(0, rec.voidCredits + Math.max(0, Math.floor(amount)));
ledger.handles[hk] = rec;
writeLedger(ledger);
return rec.voidCredits;
}
/** Return all handle records for admin views. */
export function getAllHandles(): Record<string, HandleRecord> {
return readLedger().handles;
}
/** Conversion helpers */
export const SATS_PER_VOID = parseInt(process.env.VOID_CREDIT_SATS_PER ?? "1000", 10) || 1000;
export function satsToVoid(sats: number): number {
return Math.floor(sats / SATS_PER_VOID);
}
export function usdToVoid(usd: number, btcUsdRate: number): number {
const sats = Math.floor((usd / btcUsdRate) * 1e8);
return satsToVoid(sats);
}

View File

@@ -9,6 +9,7 @@ export const SITE_NAV_GROUPS: SiteNavGroup[] = [
title: "Hub & account",
items: [
{ label: "Hub", href: "/", icon: "⌂" },
{ label: "Master directory", href: "/directory", icon: "🧭" },
{ label: "Dashboard", href: "/dashboard", icon: "📊" },
{ label: "Sign in", href: "/sign-in", icon: "🔑" },
{ label: "Register", href: "/sign-up", icon: "✎" },
@@ -50,6 +51,18 @@ export const SITE_NAV_GROUPS: SiteNavGroup[] = [
{ label: "Barter", href: "/barter", icon: "♻️" },
],
},
{
title: "VOID Tools & Labs",
items: [
{ label: "VOID Tools (unlock)", href: "/tools", icon: "🔓" },
{ label: "Void Labs", href: "/labs", icon: "🧪" },
{ label: "Cipher Academy", href: "/academy", icon: "🎓" },
{ label: "The Codex", href: "/codex", icon: "📖" },
{ label: "The Oracle", href: "/oracle", icon: "🔮" },
{ label: "The Nexus", href: "/nexus", icon: "🕸️" },
{ label: "DarkHost", href: "/hosting", icon: "🖥️" },
],
},
{
title: "Intel & tools",
items: [

136
lib/toolsCatalog.ts Normal file
View File

@@ -0,0 +1,136 @@
/**
* CyberLux tools catalog.
* Tools are unlocked permanently per handle by spending VOID credits server-side.
*/
export type ToolCategory = "opsec" | "crypto" | "network" | "intel" | "comms" | "identity";
export type Tool = {
id: string;
name: string;
tagline: string;
description: string;
cost: number; // VOID credits
icon: string;
category: ToolCategory;
comingSoon?: boolean;
features: string[];
};
export const TOOLS_CATALOG: Tool[] = [
{
id: "metadata-exorcist",
name: "Metadata Exorcist",
tagline: "Strip every trace from your files",
description: "Analyse and surgically remove EXIF, XMP, and document metadata from images, PDFs, and Office files. Paste base64 or upload directly.",
cost: 5,
icon: "🧹",
category: "opsec",
features: ["EXIF / XMP removal", "PDF metadata wipe", "Batch processing", "Before/after diff view"],
},
{
id: "pgp-forge",
name: "PGP Forge",
tagline: "Generate, sign, encrypt — all in-browser",
description: "Full PGP key management suite: generate Ed25519 or RSA-4096 keypairs, sign messages, verify signatures, and encrypt / decrypt payloads.",
cost: 6,
icon: "🔐",
category: "crypto",
features: ["Ed25519 & RSA-4096", "Sign / verify", "Encrypt / decrypt", "Key import & export", "Keyring manager"],
},
{
id: "shadow-trace",
name: "Shadow Trace",
tagline: "Analyse your OPSEC exposure score",
description: "Enter a handle, domain, or BTC address. Shadow Trace correlates public darknet indexing to give you an operational security score and surface visible leaks.",
cost: 8,
icon: "👁",
category: "opsec",
features: ["Handle exposure scan", "Domain footprint", "BTC address graph", "OPSEC score + remediation"],
},
{
id: "vpn-leak-tester",
name: "Leak Oracle",
tagline: "Verify what your circuit actually exposes",
description: "Multi-vector leak tester: DNS, WebRTC, IPv6, HTTP headers, and TLS fingerprint — run inside Tor Browser to verify your circuit leaks nothing.",
cost: 10,
icon: "🕳️",
category: "network",
features: ["DNS leak check", "WebRTC detection", "IPv6 probe", "Header fingerprint", "TLS JA3 hash"],
},
{
id: "tor-circuit-inspector",
name: "Circuit Inspector",
tagline: "Visualise and audit your Tor path",
description: "Real-time Tor circuit visualisation with relay details, exit geolocation, bandwidth stats, and circuit switching controls.",
cost: 12,
icon: "🌐",
category: "network",
features: ["Live circuit map", "Relay GeoIP lookup", "Exit node audit", "Circuit rebuild trigger", "Guard node history"],
},
{
id: "breach-oracle",
name: "Breach Oracle",
tagline: "Check credentials against known dumps",
description: "Query anonymised breach indices for email addresses, usernames, and password hashes (SHA-1 k-anonymity prefix). Nothing is logged.",
cost: 15,
icon: "🗄️",
category: "intel",
features: ["Email / handle check", "HIBP-compatible API", "Password hash prefix probe", "Zero-log architecture", "Bulk CSV mode"],
},
{
id: "deep-crawler",
name: "Void Crawler Pro",
tagline: "Deep darknet site discovery engine",
description: "Expanded crawl index with link graph, freshness scores, and category clustering. Goes beyond what the public Void Crawl surface exposes.",
cost: 20,
icon: "🕷️",
category: "intel",
features: ["Extended onion index", "Link graph visualiser", "Freshness tracker", "Category filter", "Export JSON / CSV"],
},
{
id: "signal-ghost",
name: "Signal Ghost",
tagline: "Ephemeral encrypted channel generator",
description: "Create a one-shot end-to-end encrypted channel. Share the link; both parties exchange messages; channel self-destructs after timeout or after close.",
cost: 25,
icon: "👻",
category: "comms",
features: ["X25519 + AES-GCM in-browser", "Zero-server-storage", "Configurable TTL", "Screenshot-proof mode", "File attachment (≤ 5 MB)"],
},
{
id: "onion-architect",
name: "Onion Architect",
tagline: "Guided hidden service provisioning wizard",
description: "Step-by-step wizard for setting up Tor v3 hidden services: generates torrc stubs, nginx configs, systemd units, and security hardening checklists.",
cost: 35,
icon: "🧅",
category: "network",
features: ["torrc generator", "nginx vhost builder", "systemd unit wizard", "UFW hardening script", "Vanity prefix guide"],
comingSoon: false,
},
{
id: "identity-auditor",
name: "Identity Auditor",
tagline: "Full digital identity surface review",
description: "Comprehensive review of a handle or identity across known darknet forums, paste sites, and public indices. Produces a redacted report with a risk rating.",
cost: 40,
icon: "🪪",
category: "identity",
features: ["Forum cross-reference", "Paste site scan", "Writing-style consistency flag", "Risk score", "Downloadable report"],
comingSoon: true,
},
];
export function getToolById(id: string): Tool | undefined {
return TOOLS_CATALOG.find((t) => t.id === id);
}
export const CATEGORY_LABELS: Record<ToolCategory, string> = {
opsec: "OPSEC",
crypto: "Crypto",
network: "Network",
intel: "Intelligence",
comms: "Comms",
identity: "Identity",
};