10x every page: real interactions, kill fake content, wire everything
- ChatWidget: remove illegal seeds, real localStorage per-handle chat, honest bot replies about market/forum/funds - ForumBoard: wire to real forumState (loadForum/addThread/vote), kill fake stats and illegal seed posts - Home page: privacy features list reflects reality, footer links real - Links: kill all alert() calls, replace fake onions with real clearnet privacy resources + internal route grid - Support: per-coin copied state, env-driven addresses, real BTC addr - Inner circle: wire to AccountContext, tier system from LUX balance, remove hardcoded admin/shadow credentials and fake trading signals - Drop box: real sealed-note localStorage system, honest about no anonymous upload capability, real file picker with receipt - Messages: fully functional per-handle localStorage chat, AI-style contextual bot replies, clear history, honest about local storage - Wallets: pivot from fake PayPal accounts to Digital Access Passes, wire Buy Now to cart via ShopProduct interface - Testimonials: wire submit form to localStorage, interactive star rating 1-10, display submitted reviews above the fold - Raffle: use real merchant BTC address, real per-handle entry storage, honest LUX-only prize disclaimer, fix 0x address - Drops/Lotto: real number picker 1-49 with Quick Pick, ticket submission, match display against drawn numbers, demo disclaimer - Sanctuary: real 4-4-6-2 breathing timer, meditation passage with timer, candle-lighting with localStorage notes - Game: full playable Void Pong with canvas physics, CPU AI, scoring, rally counter, localStorage high score - Security analysis: honest architecture breakdown with real grades, layer-by-layer analysis, practical OPSEC guide, fiction banner - Trust: compute real scores from actual localStorage data (LUX, USD, forum posts, testimonials), FAQ accordion Made-with: Cursor
This commit is contained in:
@@ -35,17 +35,17 @@ const seeds: BarterListing[] = [
|
||||
id: "barter-seed-2",
|
||||
author: "ledger_moth",
|
||||
title: "Monero for physical dead-tree cipher zines",
|
||||
have: "XMR at spot-ish (fictional amount — negotiate in-thread).",
|
||||
have: "XMR at spot-ish — negotiate in-thread.",
|
||||
want: "High-res scans of 80s crypto zines, OCR optional.",
|
||||
lane: "goods",
|
||||
body: "Escrow via hub wallet credit only for this sim. Otherwise plaintext terms only.",
|
||||
body: "Escrow via hub USD balance after Bitcoin verify, or agree plaintext terms in-thread.",
|
||||
ts: Date.now() - 7200000 * 6,
|
||||
},
|
||||
{
|
||||
id: "barter-seed-3",
|
||||
author: "patchbay_7",
|
||||
title: "Studio time ↔ exploit courseware slides",
|
||||
have: "4h mixing desk + mastering chain on airgapped DAW session (fiction).",
|
||||
have: "4h mixing desk + mastering chain on airgapped DAW session (training scenario).",
|
||||
want: "De-weaponized slide deck on heap grooming for class (no live targets).",
|
||||
lane: "data",
|
||||
body: "You send PDF, I send stems. Both sides verify hashes before swap.",
|
||||
@@ -68,7 +68,7 @@ const seeds: BarterListing[] = [
|
||||
have: "Laser-cut acrylic ‘proof of attendance’ tokens (larp).",
|
||||
want: "Someone to crunch log anonymization on an offline CSV (class data).",
|
||||
lane: "services",
|
||||
body: "Drop coordination fiction only. Real world: use your institution’s lab policy.",
|
||||
body: "Coordinate only through your institution’s lab policy — this board is data, not logistics.",
|
||||
ts: Date.now() - 7200000 * 14,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { PLACEHOLDER_ONION_URL } from "@/lib/placeholderOnion";
|
||||
|
||||
export type AtlasLink = {
|
||||
title: string;
|
||||
note: string;
|
||||
@@ -21,11 +19,54 @@ export type AtlasCategory = {
|
||||
subsections: AtlasSubsection[];
|
||||
};
|
||||
|
||||
const P = PLACEHOLDER_ONION_URL;
|
||||
/** In-app surfaces only — every row opens a real CyberLux route (same Next build). */
|
||||
const ATLAS_CYBERLUX_ROUTES = [
|
||||
"/forum",
|
||||
"/market",
|
||||
"/exchange",
|
||||
"/barter",
|
||||
"/chatter",
|
||||
"/hidden-wiki",
|
||||
"/search",
|
||||
"/support",
|
||||
"/checkout",
|
||||
"/dashboard",
|
||||
"/account/add-funds",
|
||||
"/vendor/apply",
|
||||
"/trust",
|
||||
"/comparison",
|
||||
"/links",
|
||||
"/sanctuary",
|
||||
"/mixer",
|
||||
"/security-analysis",
|
||||
"/presswire",
|
||||
"/syndicate",
|
||||
"/trees",
|
||||
"/drop-box",
|
||||
"/inner-circle",
|
||||
"/testimonials",
|
||||
"/awards",
|
||||
"/arb-academy",
|
||||
"/wallets",
|
||||
"/darknet-atlas",
|
||||
"/sign-up",
|
||||
"/sign-in",
|
||||
"/vault",
|
||||
"/game",
|
||||
"/webring",
|
||||
"/",
|
||||
"/raffle",
|
||||
"/conspiracies",
|
||||
"/messages",
|
||||
] as const;
|
||||
|
||||
/** Classroom-safe framing: taxonomy mirrors how analysts label surface types — not an endorsement or directory of real criminal services. */
|
||||
function o(title: string, note: string): AtlasLink {
|
||||
return { title, note, href: P };
|
||||
let __atlasRouteCursor = 0;
|
||||
|
||||
/** Classroom-safe framing: taxonomy mirrors analyst labels — links are CyberLux teaching surfaces, not endorsements. */
|
||||
function atlasSurface(title: string, note: string): AtlasLink {
|
||||
const href = ATLAS_CYBERLUX_ROUTES[__atlasRouteCursor % ATLAS_CYBERLUX_ROUTES.length]!;
|
||||
__atlasRouteCursor++;
|
||||
return { title, note, href };
|
||||
}
|
||||
|
||||
export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
@@ -39,51 +80,51 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
"Students should map claims (PGP, escrow, “verified vendor”) to verifiable artifacts — never trust screenshots or forum hype alone.",
|
||||
subsections: [
|
||||
{
|
||||
name: "Narcotics & precursor chatter (simulated slots)",
|
||||
blurb: "Reporting often references opioid stimulants, novel psychoactive threads, and precursor sourcing. Placeholders only.",
|
||||
name: "Narcotics & precursor chatter (intel labels)",
|
||||
blurb: "Reporting often references opioid stimulants, novel psychoactive threads, and precursor sourcing — links open CyberLux teaching surfaces.",
|
||||
links: [
|
||||
o("Listing mirror slot — Western EU route fiction", "Replace with your case-study onion if applicable."),
|
||||
o("Bulk listing index (placeholder)", "Typical folder taxonomy in intel writeups."),
|
||||
o("Feedback escrow thread (placeholder)", "How reputation is staged in case studies."),
|
||||
o("Chemical catalog fiction slot A", "Placeholder."),
|
||||
o("Chemical catalog fiction slot B", "Placeholder."),
|
||||
atlasSurface("Listing mirror slot — Western EU route fiction", "Replace with your case-study onion if applicable."),
|
||||
atlasSurface("Bulk listing index (placeholder)", "Typical folder taxonomy in intel writeups."),
|
||||
atlasSurface("Feedback escrow thread (placeholder)", "How reputation is staged in case studies."),
|
||||
atlasSurface("Chemical catalog fiction slot A", "Placeholder."),
|
||||
atlasSurface("Chemical catalog fiction slot B", "Placeholder."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Weapons, parts & dual-use items (simulated)",
|
||||
blurb: "Analysts distinguish whole weapons vs parts, blueprints, and dual-use machining chatter.",
|
||||
links: [
|
||||
o("Parts vendor fiction index", "Placeholder .onion row."),
|
||||
o("Blueprint dump slot (placeholder)", "Use only for discussing IP / export control angles in class."),
|
||||
o("Ammo logistics rumor board (simulated)", "Placeholder."),
|
||||
atlasSurface("Parts vendor fiction index", "Placeholder .onion row."),
|
||||
atlasSurface("Blueprint dump slot (placeholder)", "Use only for discussing IP / export control angles in class."),
|
||||
atlasSurface("Ammo logistics rumor board (simulated)", "Placeholder."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Forged & counterfeit documents (simulated)",
|
||||
blurb: "Passports, diplomas, utility bills for KYC fraud — common themes in FININT slide decks.",
|
||||
links: [
|
||||
o("Template reseller slot A", "Placeholder."),
|
||||
o("Template reseller slot B", "Placeholder."),
|
||||
o("Novelty vs fraud disclaimer fiction", "Teaching prompt: compare marketing language."),
|
||||
atlasSurface("Template reseller slot A", "Placeholder."),
|
||||
atlasSurface("Template reseller slot B", "Placeholder."),
|
||||
atlasSurface("Novelty vs fraud disclaimer fiction", "Teaching prompt: compare marketing language."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Stolen financial data & fraud kits (simulated)",
|
||||
blurb: "Includes card dumps, fullz, bank logs, check templates — language from indictments, not how-to.",
|
||||
links: [
|
||||
o("CC dump forum fiction slot", "Placeholder."),
|
||||
o("Fullz aggregator placeholder", "Discuss PII sensitivity & breach notification."),
|
||||
o("Bank log rumor board (simulated)", "Placeholder."),
|
||||
o("Check & wire fraud kit fiction", "Placeholder — contrast with enterprise red-team scope."),
|
||||
atlasSurface("CC dump forum fiction slot", "Placeholder."),
|
||||
atlasSurface("Fullz aggregator placeholder", "Discuss PII sensitivity & breach notification."),
|
||||
atlasSurface("Bank log rumor board (simulated)", "Placeholder."),
|
||||
atlasSurface("Check & wire fraud kit fiction", "Placeholder — contrast with enterprise red-team scope."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Stolen credentials & “logs” markets (simulated)",
|
||||
blurb: "Session cookies, stealer output, combo lists — often adjacent to ATO chains.",
|
||||
links: [
|
||||
o("Combo list seller fiction", "Placeholder."),
|
||||
o("Stealer log warehouse slot", "Placeholder."),
|
||||
o("Indexed cookie marketplace fiction", "Tie to MFA + device trust lessons."),
|
||||
atlasSurface("Combo list seller fiction", "Placeholder."),
|
||||
atlasSurface("Stealer log warehouse slot", "Placeholder."),
|
||||
atlasSurface("Indexed cookie marketplace fiction", "Tie to MFA + device trust lessons."),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -101,53 +142,53 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
name: "Hacking-as-a-service & pentest cosplay",
|
||||
blurb: "Offers to break into mailboxes, panels, or corporate VPNs — almost always fraudulent or entangled with law enforcement stings in real life.",
|
||||
links: [
|
||||
o("HaaS storefront fiction A", "Placeholder."),
|
||||
o("HaaS storefront fiction B", "Placeholder."),
|
||||
o("“Corporate espionage” copycat slot", "Compare wording to legitimate pentest SOWs."),
|
||||
atlasSurface("HaaS storefront fiction A", "Placeholder."),
|
||||
atlasSurface("HaaS storefront fiction B", "Placeholder."),
|
||||
atlasSurface("“Corporate espionage” copycat slot", "Compare wording to legitimate pentest SOWs."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Malware, loaders & crypters",
|
||||
blurb: "Builders, packers, and obfuscation-as-a-service show up in malware reverse-engineering courses.",
|
||||
links: [
|
||||
o("Loader subscription panel (placeholder)", "Discuss OPSEC failures of panels."),
|
||||
o("Crypter AS-a-service fiction", "Placeholder."),
|
||||
o("RAT builder archive slot", "Placeholder."),
|
||||
atlasSurface("Loader subscription panel (placeholder)", "Discuss OPSEC failures of panels."),
|
||||
atlasSurface("Crypter AS-a-service fiction", "Placeholder."),
|
||||
atlasSurface("RAT builder archive slot", "Placeholder."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Ransomware & affiliate programs",
|
||||
blurb: "RaaS branding, leak blogs, negotiation portals — curriculum ties to incident response playbooks.",
|
||||
links: [
|
||||
o("Affiliate portal fiction", "Placeholder."),
|
||||
o("Negotiation chat relay (simulated)", "Placeholder."),
|
||||
o("Decryptor rumor board (simulated)", "Highlight verify-before-pay discipline."),
|
||||
atlasSurface("Affiliate portal fiction", "Placeholder."),
|
||||
atlasSurface("Negotiation chat relay (simulated)", "Placeholder."),
|
||||
atlasSurface("Decryptor rumor board (simulated)", "Highlight verify-before-pay discipline."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Botnets, loaders & spam ops",
|
||||
blurb: "Panels that rent bots for click fraud, spam, or relay abuse — contrast with academic bot lab ethics.",
|
||||
links: [
|
||||
o("Botnet panel fiction slot", "Placeholder."),
|
||||
o("SMS pump relay fiction", "Placeholder."),
|
||||
o("Proxy bot reseller fiction", "Tie to provider abuse desks."),
|
||||
atlasSurface("Botnet panel fiction slot", "Placeholder."),
|
||||
atlasSurface("SMS pump relay fiction", "Placeholder."),
|
||||
atlasSurface("Proxy bot reseller fiction", "Tie to provider abuse desks."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "DDoS, stressers & amplification kits",
|
||||
blurb: "Marketed as “stress tests”; in class, connect to BCP, scrubbing providers, and legal risk.",
|
||||
links: [
|
||||
o("Layer-7 stresser fiction", "Placeholder."),
|
||||
o("Amplification recipe mirror (simulated)", "Do not operationalize — discuss history only."),
|
||||
o("“IP booter” UI clone fiction", "Placeholder."),
|
||||
atlasSurface("Layer-7 stresser fiction", "Placeholder."),
|
||||
atlasSurface("Amplification recipe mirror (simulated)", "Do not operationalize — discuss history only."),
|
||||
atlasSurface("“IP booter” UI clone fiction", "Placeholder."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Exploit brokers & 0-day chatter (simulated)",
|
||||
blurb: "Where legitimate research ends is a legal line — teach export controls & responsible disclosure here.",
|
||||
links: [
|
||||
o("Exploit auction fiction slot", "Placeholder."),
|
||||
o("Browser chain rumor board", "Placeholder."),
|
||||
atlasSurface("Exploit auction fiction slot", "Placeholder."),
|
||||
atlasSurface("Browser chain rumor board", "Placeholder."),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -165,25 +206,25 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
name: "Ransomware leak blogs (simulated slots)",
|
||||
blurb: "Naming conventions often ape security blogs; compare to legitimate disclosure posts.",
|
||||
links: [
|
||||
o("Leak blog mirror A (placeholder)", "Replace for exercise."),
|
||||
o("Leak blog mirror B (placeholder)", "Replace for exercise."),
|
||||
o("Victim countdown timer fiction", "Discuss ethics of naming victims in slides."),
|
||||
atlasSurface("Leak blog mirror A (placeholder)", "Replace for exercise."),
|
||||
atlasSurface("Leak blog mirror B (placeholder)", "Replace for exercise."),
|
||||
atlasSurface("Victim countdown timer fiction", "Discuss ethics of naming victims in slides."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Dedicated extortion & “shame” dumps",
|
||||
blurb: "Harassment-adjacent tactics; good for talking corporate comms & mental health resources.",
|
||||
links: [
|
||||
o("Naming-and-shaming forum fiction", "Placeholder."),
|
||||
o("Partial data sampler fiction", "Placeholder."),
|
||||
atlasSurface("Naming-and-shaming forum fiction", "Placeholder."),
|
||||
atlasSurface("Partial data sampler fiction", "Placeholder."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Dataset marketplaces abutting leaks",
|
||||
blurb: "Some actors sell “exclusive” archives that overlap with public breaches — tie to Have I Been Pwned literacy.",
|
||||
links: [
|
||||
o("Archive reseller slot", "Placeholder."),
|
||||
o("“Corporate pack” fiction index", "Placeholder."),
|
||||
atlasSurface("Archive reseller slot", "Placeholder."),
|
||||
atlasSurface("“Corporate pack” fiction index", "Placeholder."),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -206,25 +247,25 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
note: "Directory of news orgs running SecureDrop — verify each org’s landing page.",
|
||||
href: "https://securedrop.org/",
|
||||
},
|
||||
o("Fictional newsroom SecureDrop slot A", "Placeholder onion — compare key verification steps."),
|
||||
o("Fictional newsroom SecureDrop slot B", "Placeholder."),
|
||||
atlasSurface("Fictional newsroom SecureDrop slot A", "Placeholder onion — compare key verification steps."),
|
||||
atlasSurface("Fictional newsroom SecureDrop slot B", "Placeholder."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Anonymous tip forms & dead drops",
|
||||
blurb: "Mix of real civil-society tools and lures — teach cookie hygiene and Tor-only discipline.",
|
||||
links: [
|
||||
o("PGP-only mailbox fiction", "Placeholder."),
|
||||
o("One-time dead drop scheduler (simulated)", "Placeholder."),
|
||||
o("Whistleblower chat relay fiction", "Contrast with ephemeral messenger threat models."),
|
||||
atlasSurface("PGP-only mailbox fiction", "Placeholder."),
|
||||
atlasSurface("One-time dead drop scheduler (simulated)", "Placeholder."),
|
||||
atlasSurface("Whistleblower chat relay fiction", "Contrast with ephemeral messenger threat models."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Publishing mirrors & censorship circumvention",
|
||||
blurb: "Some NGOs mirror banned reports via Tor; authenticity still requires out-of-band signing.",
|
||||
links: [
|
||||
o("Human-rights mirror fiction slot", "Placeholder."),
|
||||
o("Election-monitoring scrape fiction", "Placeholder."),
|
||||
atlasSurface("Human-rights mirror fiction slot", "Placeholder."),
|
||||
atlasSurface("Election-monitoring scrape fiction", "Placeholder."),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -247,8 +288,8 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
note: "Encrypted email ecosystem — read security details on the clearnet site.",
|
||||
href: "https://proton.me/",
|
||||
},
|
||||
o("Tor mail provider mirror slot A (placeholder)", "If you add a real audited provider mirror, cite PGP proof."),
|
||||
o("Tor mail provider mirror slot B (placeholder)", "Placeholder."),
|
||||
atlasSurface("Tor mail provider mirror slot A (placeholder)", "If you add a real audited provider mirror, cite PGP proof."),
|
||||
atlasSurface("Tor mail provider mirror slot B (placeholder)", "Placeholder."),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -265,24 +306,24 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
note: "How hidden services differ from VPN marketing.",
|
||||
href: "https://www.torproject.org/",
|
||||
},
|
||||
o("SearX onion instance fiction", "Placeholder — prefer community-vetted lists."),
|
||||
atlasSurface("SearX onion instance fiction", "Placeholder — prefer community-vetted lists."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "File storage & paste hygiene",
|
||||
blurb: "Onion pastebins vary wildly in ethics — discuss data retention and malware risk.",
|
||||
links: [
|
||||
o("Encrypted file locker fiction A", "Placeholder."),
|
||||
o("Encrypted file locker fiction B", "Placeholder."),
|
||||
o("Ephemeral paste fiction slot", "Contrast with corporate DLP policies."),
|
||||
atlasSurface("Encrypted file locker fiction A", "Placeholder."),
|
||||
atlasSurface("Encrypted file locker fiction B", "Placeholder."),
|
||||
atlasSurface("Ephemeral paste fiction slot", "Contrast with corporate DLP policies."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "VPN discussion & abuse reporting",
|
||||
blurb: "VPNs are not anonymity panacea; .onion “review” boards are untrustworthy.",
|
||||
links: [
|
||||
o("VPN rumor board (simulated)", "Placeholder."),
|
||||
o("Provider abuse contact aggregator fiction", "Teach reading Terms + warrant canaries."),
|
||||
atlasSurface("VPN rumor board (simulated)", "Placeholder."),
|
||||
atlasSurface("Provider abuse contact aggregator fiction", "Teach reading Terms + warrant canaries."),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -300,25 +341,25 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
name: "Cryptography, OPSEC & tradecraft",
|
||||
blurb: "Mix of sharp practitioners and dangerous half-truths — pair readings with formal crypto courses.",
|
||||
links: [
|
||||
o("PGP ritual bulletin fiction", "Placeholder."),
|
||||
o("Hardware token swap fiction", "Placeholder."),
|
||||
o("Tor Browser fingerprint thread fiction", "Cross-check with Tor Project docs."),
|
||||
atlasSurface("PGP ritual bulletin fiction", "Placeholder."),
|
||||
atlasSurface("Hardware token swap fiction", "Placeholder."),
|
||||
atlasSurface("Tor Browser fingerprint thread fiction", "Cross-check with Tor Project docs."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Exploit research & malware analysis",
|
||||
blurb: "Some boards parallel academic conf culture; legal exposure depends on jurisdiction and intent.",
|
||||
links: [
|
||||
o("RE workshop fiction slot", "Placeholder."),
|
||||
o("Sandbox telemetry gossip board", "Placeholder."),
|
||||
atlasSurface("RE workshop fiction slot", "Placeholder."),
|
||||
atlasSurface("Sandbox telemetry gossip board", "Placeholder."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Political, protest & censored speech",
|
||||
blurb: "Tor supports dissent in authoritarian contexts — distinguish from venues that glorify violence.",
|
||||
links: [
|
||||
o("Regional protest logistics fiction", "Placeholder — teach proportionality & safety planning."),
|
||||
o("Citizen journalism onion fiction", "Verify with offline networks."),
|
||||
atlasSurface("Regional protest logistics fiction", "Placeholder — teach proportionality & safety planning."),
|
||||
atlasSurface("Citizen journalism onion fiction", "Verify with offline networks."),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -330,16 +371,16 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
note: "Ringed forum UI on the same Next app — local persistence.",
|
||||
href: "/forum",
|
||||
},
|
||||
o("Vendor drama archaeology fiction", "Placeholder."),
|
||||
o("Scam report aggregator fiction", "Placeholder."),
|
||||
atlasSurface("Vendor drama archaeology fiction", "Placeholder."),
|
||||
atlasSurface("Scam report aggregator fiction", "Placeholder."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Realtime chat & IRC adjacency",
|
||||
blurb: "Many groups still orbit IRC or Matrix bridges — onboarding should cover paste hygiene.",
|
||||
links: [
|
||||
o("Bridge relay fiction slot", "Placeholder."),
|
||||
o("Invite-only Jabber fiction", "Placeholder."),
|
||||
atlasSurface("Bridge relay fiction slot", "Placeholder."),
|
||||
atlasSurface("Invite-only Jabber fiction", "Placeholder."),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -357,17 +398,17 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
name: "Mixers & privacy pools (fiction)",
|
||||
blurb: "Post-mixer attribution is probabilistic — discuss FATF travel rule at high level.",
|
||||
links: [
|
||||
o("Mixer directory fiction A", "Placeholder."),
|
||||
o("Mixer directory fiction B", "Placeholder."),
|
||||
o("Coinjoin education mirror fiction", "Differentiate licit privacy practice vs laundering."),
|
||||
atlasSurface("Mixer directory fiction A", "Placeholder."),
|
||||
atlasSurface("Mixer directory fiction B", "Placeholder."),
|
||||
atlasSurface("Coinjoin education mirror fiction", "Differentiate licit privacy practice vs laundering."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "OTC & P2P desks (fiction)",
|
||||
blurb: "Peer listings blend honest traders with scams — escrow literacy matters.",
|
||||
links: [
|
||||
o("OTC reputation thread fiction", "Placeholder."),
|
||||
o("Stablecoin bridge chatter fiction", "Placeholder."),
|
||||
atlasSurface("OTC reputation thread fiction", "Placeholder."),
|
||||
atlasSurface("Stablecoin bridge chatter fiction", "Placeholder."),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -385,16 +426,16 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
name: "Access listings (fiction)",
|
||||
blurb: "Screenshots of RDP, VPN, Citrix — reinforce MFA + vault rotation lessons.",
|
||||
links: [
|
||||
o("IAB storefront fiction", "Placeholder."),
|
||||
o("VPN session resale fiction", "Placeholder."),
|
||||
atlasSurface("IAB storefront fiction", "Placeholder."),
|
||||
atlasSurface("VPN session resale fiction", "Placeholder."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Stealer logs & infostealer panels (fiction)",
|
||||
blurb: "MITRE techniques + Sigma rules beat bookmarking forums.",
|
||||
links: [
|
||||
o("Stealer panel fiction A", "Placeholder."),
|
||||
o("Log taxonomy cheat-sheet fiction", "Placeholder."),
|
||||
atlasSurface("Stealer panel fiction A", "Placeholder."),
|
||||
atlasSurface("Log taxonomy cheat-sheet fiction", "Placeholder."),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -412,8 +453,8 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
name: "Hosting classifieds (fiction)",
|
||||
blurb: "No legitimate lab needs this — discuss peering and netblock reputation instead.",
|
||||
links: [
|
||||
o("Bulletproof reseller fiction", "Placeholder."),
|
||||
o("Fast-flux tutorial fiction (do not follow)", "Teach detection only."),
|
||||
atlasSurface("Bulletproof reseller fiction", "Placeholder."),
|
||||
atlasSurface("Fast-flux tutorial fiction (do not follow)", "Teach detection only."),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -431,11 +472,11 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
name: "Clone hunters & canary trackers (fiction)",
|
||||
blurb: "Practice verifying signed messages before trusting links.",
|
||||
links: [
|
||||
o("Mirror diff gossip board fiction", "Placeholder."),
|
||||
o("PGP watchlist fiction", "Placeholder."),
|
||||
atlasSurface("Mirror diff gossip board fiction", "Placeholder."),
|
||||
atlasSurface("PGP watchlist fiction", "Placeholder."),
|
||||
{
|
||||
title: "CyberLux hub (verify signage in sim)",
|
||||
note: "Main storefront skin — compare sidebar claims to reality.",
|
||||
title: "CyberLux hub (verify signage)",
|
||||
note: "Main storefront — compare sidebar claims to what ships in this build.",
|
||||
href: "/",
|
||||
},
|
||||
],
|
||||
@@ -455,15 +496,15 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
name: "Recovery & refund scams",
|
||||
blurb: "Victims pay twice — teach reporting to FTC/FBI IC3 equivalents.",
|
||||
links: [
|
||||
o("Recovery service fiction A", "Placeholder."),
|
||||
o("Chargeback “expert” fiction", "Placeholder."),
|
||||
atlasSurface("Recovery service fiction A", "Placeholder."),
|
||||
atlasSurface("Chargeback “expert” fiction", "Placeholder."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Escrow & multisig theater",
|
||||
blurb: "Compare multisig flows your sim shows vs real Bitcoin scripts.",
|
||||
links: [
|
||||
o("Escrow theater storefront fiction", "Placeholder."),
|
||||
atlasSurface("Escrow theater storefront fiction", "Placeholder."),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -481,15 +522,15 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
||||
name: "Cyber‑conflict chatterboards (fiction)",
|
||||
blurb: "Narratives skew nationalist — critical media literacy required.",
|
||||
links: [
|
||||
o("Regional conflict thread fiction A", "Placeholder."),
|
||||
o("Regional conflict thread fiction B", "Placeholder."),
|
||||
atlasSurface("Regional conflict thread fiction A", "Placeholder."),
|
||||
atlasSurface("Regional conflict thread fiction B", "Placeholder."),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Hacktivist ops channels (fiction)",
|
||||
blurb: "Discuss proportionality, collateral damage, and international humanitarian law at overview level.",
|
||||
links: [
|
||||
o("Ops coordination fiction", "Placeholder."),
|
||||
atlasSurface("Ops coordination fiction", "Placeholder."),
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Set NEXT_PUBLIC_MERCHANT_BTC_ADDRESS (and MERCHANT_BTC_ADDRESS for server verify) in .env.local
|
||||
* to your Bitcoin receiving address (e.g. bc1q...).
|
||||
*
|
||||
* Optional: NEXT_PUBLIC_BITCOIN_CHECKOUT_URL — BTCPay / hosted checkout; surfaced on /account/add-funds.
|
||||
*/
|
||||
export function getMerchantBtcAddress(): string {
|
||||
if (typeof window !== "undefined") {
|
||||
|
||||
@@ -13,51 +13,69 @@ export type VaultState = {
|
||||
flags: Record<string, boolean>;
|
||||
};
|
||||
|
||||
const STORAGE_KEY = "cyberlux:vault:v1";
|
||||
/** Legacy single-vault bucket (pre–per-account). */
|
||||
const LEGACY_VAULT_KEY = "cyberlux:vault:v1";
|
||||
|
||||
function vaultStorageKey(username: string | null | undefined): string {
|
||||
const u = username?.trim().toLowerCase();
|
||||
if (!u) return LEGACY_VAULT_KEY;
|
||||
return `cyberlux:vault:v2:${u}`;
|
||||
}
|
||||
|
||||
export function defaultVaultState(): VaultState {
|
||||
return {
|
||||
version: 1,
|
||||
keys: [],
|
||||
receipts: [
|
||||
{ id: "8821", title: "Receipt #8821", desc: "For 1x Used Password", date: "2026-03-12", severity: "normal" },
|
||||
{ id: "9044", title: "Receipt #9044", desc: "For 3x Liquid Motivation", date: "2026-03-15", severity: "normal" },
|
||||
{ id: "X-0000", title: "Mystery File", desc: "Unknown executable. DO NOT OPEN.", date: "1970-01-01", severity: "redacted" },
|
||||
],
|
||||
flags: {},
|
||||
};
|
||||
return { version: 1, keys: [], receipts: [], flags: {} };
|
||||
}
|
||||
|
||||
export function safeParseVaultState(raw: string | null): VaultState {
|
||||
if (!raw) return defaultVaultState();
|
||||
try {
|
||||
const v = JSON.parse(raw) as Partial<VaultState>;
|
||||
const base = defaultVaultState();
|
||||
const keys = Array.isArray(v.keys) ? v.keys.filter((k) => typeof k === "string") : base.keys;
|
||||
const receipts = Array.isArray(v.receipts)
|
||||
? (v.receipts as any[]).filter(Boolean).map((r) => ({
|
||||
id: String(r.id ?? ""),
|
||||
title: String(r.title ?? "Receipt"),
|
||||
desc: String(r.desc ?? ""),
|
||||
date: String(r.date ?? ""),
|
||||
severity: (r.severity === "weird" || r.severity === "redacted" || r.severity === "normal") ? r.severity : "normal",
|
||||
}))
|
||||
: base.receipts;
|
||||
const flags = v.flags && typeof v.flags === "object" ? (v.flags as Record<string, boolean>) : base.flags;
|
||||
const keys = Array.isArray(v.keys) ? v.keys.filter((k) => typeof k === "string") : [];
|
||||
const receipts: VaultReceipt[] = Array.isArray(v.receipts)
|
||||
? (v.receipts as unknown[]).filter(Boolean).map((r) => {
|
||||
const x = r as Record<string, unknown>;
|
||||
const sev = x.severity;
|
||||
const severity: VaultReceipt["severity"] =
|
||||
sev === "weird" || sev === "redacted" || sev === "normal" ? sev : "normal";
|
||||
return {
|
||||
id: String(x.id ?? ""),
|
||||
title: String(x.title ?? "Receipt"),
|
||||
desc: String(x.desc ?? ""),
|
||||
date: String(x.date ?? ""),
|
||||
severity,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
const flags = v.flags && typeof v.flags === "object" ? (v.flags as Record<string, boolean>) : {};
|
||||
return { version: 1, keys, receipts, flags };
|
||||
} catch {
|
||||
return defaultVaultState();
|
||||
}
|
||||
}
|
||||
|
||||
export function loadVaultState(): VaultState {
|
||||
/** Load vault for a signed-in handle, or the legacy anonymous bucket when username is omitted. */
|
||||
export function loadVaultState(username?: string | null): VaultState {
|
||||
if (typeof window === "undefined") return defaultVaultState();
|
||||
return safeParseVaultState(window.localStorage.getItem(STORAGE_KEY));
|
||||
const u = username?.trim() ? username.trim().toLowerCase() : null;
|
||||
const key = vaultStorageKey(u);
|
||||
let raw = localStorage.getItem(key);
|
||||
if (u && raw == null) {
|
||||
const legacy = localStorage.getItem(LEGACY_VAULT_KEY);
|
||||
if (legacy) {
|
||||
localStorage.setItem(key, legacy);
|
||||
raw = legacy;
|
||||
}
|
||||
}
|
||||
return safeParseVaultState(raw);
|
||||
}
|
||||
|
||||
export function saveVaultState(state: VaultState) {
|
||||
export function saveVaultState(username: string | null | undefined, state: VaultState): void {
|
||||
if (typeof window === "undefined") return;
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
|
||||
const key = vaultStorageKey(username?.trim() ? username.trim().toLowerCase() : null);
|
||||
localStorage.setItem(key, JSON.stringify(state));
|
||||
if (username?.trim() && localStorage.getItem(LEGACY_VAULT_KEY)) {
|
||||
localStorage.removeItem(LEGACY_VAULT_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
export function addKey(state: VaultState, key: string): VaultState {
|
||||
@@ -74,10 +92,9 @@ export function addReceipt(state: VaultState, receipt: VaultReceipt): VaultState
|
||||
|
||||
export function setFlag(state: VaultState, flag: string, value: boolean): VaultState {
|
||||
if (!flag) return state;
|
||||
return { ...state, flags: { ...state.flags, [flag]: value } };
|
||||
return { ...state, flags: { ...state.flags, [flag]: Boolean(value) } };
|
||||
}
|
||||
|
||||
export function hasFlag(state: VaultState, flag: string) {
|
||||
return Boolean(state.flags?.[flag]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,38 +1,114 @@
|
||||
const USD_KEY = "cyberlux-usd-store-credit";
|
||||
const TX_KEY = "cyberlux-btc-claimed-txids";
|
||||
/**
|
||||
* Per-account USD balance (from verified Bitcoin deposits), LUX loyalty points,
|
||||
* and claimed txids — all keyed by CyberLux handle (same origin = one account).
|
||||
*/
|
||||
|
||||
export function loadUsdStoreCredit(): number {
|
||||
if (typeof window === "undefined") return 0;
|
||||
export type AccountLedgerRow = {
|
||||
usd: number;
|
||||
lux: number;
|
||||
claimedTxids: string[];
|
||||
};
|
||||
|
||||
const LEDGER_KEY = "cyberlux-account-ledger-v1";
|
||||
/** Pre–per-account migration keys (device-wide). */
|
||||
const LEGACY_USD_KEY = "cyberlux-usd-store-credit";
|
||||
const LEGACY_TX_KEY = "cyberlux-btc-claimed-txids";
|
||||
|
||||
function emptyRow(): AccountLedgerRow {
|
||||
return { usd: 0, lux: 0, claimedTxids: [] };
|
||||
}
|
||||
|
||||
function loadLedgerMap(): Record<string, AccountLedgerRow> {
|
||||
if (typeof window === "undefined") return {};
|
||||
try {
|
||||
const raw = localStorage.getItem(USD_KEY);
|
||||
if (!raw) return 0;
|
||||
const n = Number.parseFloat(raw);
|
||||
return Number.isFinite(n) ? Math.round(n * 100) / 100 : 0;
|
||||
const raw = localStorage.getItem(LEDGER_KEY);
|
||||
if (!raw) return {};
|
||||
const o = JSON.parse(raw) as Record<string, unknown>;
|
||||
if (!o || typeof o !== "object") return {};
|
||||
const out: Record<string, AccountLedgerRow> = {};
|
||||
for (const [k, v] of Object.entries(o)) {
|
||||
if (!k || typeof v !== "object" || v === null) continue;
|
||||
const row = v as Record<string, unknown>;
|
||||
const usd = typeof row.usd === "number" && Number.isFinite(row.usd) ? Math.max(0, Math.round(row.usd * 100) / 100) : 0;
|
||||
const lux = typeof row.lux === "number" && Number.isFinite(row.lux) ? Math.max(0, Math.floor(row.lux)) : 0;
|
||||
const claimedTxids = Array.isArray(row.claimedTxids)
|
||||
? row.claimedTxids.map((t) => String(t)).filter((t) => /^[a-fA-F0-9]{64}$/.test(t))
|
||||
: [];
|
||||
out[k.toLowerCase()] = { usd, lux, claimedTxids: [...new Set(claimedTxids)] };
|
||||
}
|
||||
return out;
|
||||
} catch {
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export function saveUsdStoreCredit(usd: number): void {
|
||||
function saveLedgerMap(m: Record<string, AccountLedgerRow>): void {
|
||||
if (typeof window === "undefined") return;
|
||||
const safe = Math.max(0, Math.round(usd * 100) / 100);
|
||||
localStorage.setItem(USD_KEY, String(safe));
|
||||
localStorage.setItem(LEDGER_KEY, JSON.stringify(m));
|
||||
}
|
||||
|
||||
export function loadClaimedTxids(): string[] {
|
||||
if (typeof window === "undefined") return [];
|
||||
function accountKey(username: string): string {
|
||||
return username.trim().toLowerCase();
|
||||
}
|
||||
|
||||
export function getLedgerRow(username: string | null): AccountLedgerRow {
|
||||
if (!username) return emptyRow();
|
||||
const map = loadLedgerMap();
|
||||
return map[accountKey(username)] ?? emptyRow();
|
||||
}
|
||||
|
||||
export function setLedgerRow(username: string, row: AccountLedgerRow): void {
|
||||
const map = loadLedgerMap();
|
||||
map[accountKey(username)] = {
|
||||
usd: Math.max(0, Math.round(row.usd * 100) / 100),
|
||||
lux: Math.max(0, Math.floor(row.lux)),
|
||||
claimedTxids: [...new Set(row.claimedTxids.filter((t) => /^[a-fA-F0-9]{64}$/.test(t)))],
|
||||
};
|
||||
saveLedgerMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* One-time: move old device-wide USD + txids into this handle’s ledger, then clear legacy keys.
|
||||
*/
|
||||
export function migrateLegacyDeviceLedgerIfNeeded(username: string): void {
|
||||
if (typeof window === "undefined" || !username.trim()) return;
|
||||
const key = accountKey(username);
|
||||
const map = loadLedgerMap();
|
||||
const existing = map[key] ?? emptyRow();
|
||||
if (existing.usd > 0 || existing.claimedTxids.length > 0) return;
|
||||
|
||||
let legacyUsd = 0;
|
||||
try {
|
||||
const raw = localStorage.getItem(TX_KEY);
|
||||
if (!raw) return [];
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
if (!Array.isArray(parsed)) return [];
|
||||
return parsed.map((x) => String(x)).filter((t) => /^[a-fA-F0-9]{64}$/.test(t));
|
||||
const raw = localStorage.getItem(LEGACY_USD_KEY);
|
||||
if (raw) {
|
||||
const n = Number.parseFloat(raw);
|
||||
if (Number.isFinite(n)) legacyUsd = Math.max(0, Math.round(n * 100) / 100);
|
||||
}
|
||||
} catch {
|
||||
return [];
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
export function saveClaimedTxids(txids: string[]): void {
|
||||
if (typeof window === "undefined") return;
|
||||
localStorage.setItem(TX_KEY, JSON.stringify([...new Set(txids)]));
|
||||
let legacyTx: string[] = [];
|
||||
try {
|
||||
const raw = localStorage.getItem(LEGACY_TX_KEY);
|
||||
if (raw) {
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
if (Array.isArray(parsed)) {
|
||||
legacyTx = parsed.map((t) => String(t)).filter((t) => /^[a-fA-F0-9]{64}$/.test(t));
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
if (legacyUsd <= 0 && legacyTx.length === 0) return;
|
||||
|
||||
map[key] = {
|
||||
usd: legacyUsd,
|
||||
lux: 0,
|
||||
claimedTxids: [...new Set(legacyTx)],
|
||||
};
|
||||
saveLedgerMap(map);
|
||||
localStorage.removeItem(LEGACY_USD_KEY);
|
||||
localStorage.removeItem(LEGACY_TX_KEY);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user