Add persistent onion key backup and restore, improve startup resilience, and flesh out the major site verticals with richer navigation, search coverage, and operator documentation. Made-with: Cursor
119 lines
3.6 KiB
TypeScript
119 lines
3.6 KiB
TypeScript
export type BarterLane = "goods" | "services" | "data" | "open";
|
||
|
||
export type BarterListing = {
|
||
id: string;
|
||
author: string;
|
||
title: string;
|
||
/** What the poster brings to the table */
|
||
have: string;
|
||
/** What they want back (goods, labor, crypto, favor, etc.) */
|
||
want: string;
|
||
lane: BarterLane;
|
||
body: string;
|
||
ts: number;
|
||
};
|
||
|
||
const KEY = "cyberlux-barter-v1";
|
||
|
||
function uid(): string {
|
||
if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
|
||
return `b_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
||
}
|
||
|
||
const seeds: BarterListing[] = [
|
||
{
|
||
id: "barter-seed-1",
|
||
author: "relay_op",
|
||
title: "Entropy dongles → signed opsec consult",
|
||
have: "Two hardware RNG sticks, sealed.",
|
||
want: "2h walkthrough on compartmentalized laptop setup (signals only).",
|
||
lane: "services",
|
||
body: "No clearnet video. Text + voice on agreed onion comms. PGP on first ping.",
|
||
ts: Date.now() - 7200000 * 10,
|
||
},
|
||
{
|
||
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).",
|
||
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.",
|
||
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).",
|
||
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.",
|
||
ts: Date.now() - 7200000 * 3,
|
||
},
|
||
{
|
||
id: "barter-seed-4",
|
||
author: "EU_shift",
|
||
title: "Shipping label templates → mirror canary text",
|
||
have: "Sanitized HTML snippets that look like phish (for training).",
|
||
want: "Latest signed canary paragraph from hub ring — compare byte-for-byte.",
|
||
lane: "open",
|
||
body: "Teaching red-team vs blue-team reading. No live credential harvesting.",
|
||
ts: Date.now() - 7200000 * 2,
|
||
},
|
||
{
|
||
id: "barter-seed-5",
|
||
author: "void_cartographer",
|
||
title: "Physical meet token ↔ CPU time",
|
||
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.",
|
||
ts: Date.now() - 7200000 * 14,
|
||
},
|
||
];
|
||
|
||
export function loadBarter(): BarterListing[] {
|
||
if (typeof window === "undefined") return seeds;
|
||
try {
|
||
const raw = localStorage.getItem(KEY);
|
||
if (!raw) {
|
||
saveBarter(seeds);
|
||
return seeds;
|
||
}
|
||
const v = JSON.parse(raw) as BarterListing[];
|
||
if (!Array.isArray(v) || !v.length) {
|
||
saveBarter(seeds);
|
||
return seeds;
|
||
}
|
||
return v;
|
||
} catch {
|
||
return seeds;
|
||
}
|
||
}
|
||
|
||
export function saveBarter(list: BarterListing[]): void {
|
||
if (typeof window === "undefined") return;
|
||
localStorage.setItem(KEY, JSON.stringify(list));
|
||
}
|
||
|
||
const SEED_IDS = new Set(seeds.map((s) => s.id));
|
||
|
||
export function addBarterListing(
|
||
L: Omit<BarterListing, "id" | "ts">,
|
||
): BarterListing {
|
||
const row: BarterListing = {
|
||
id: uid(),
|
||
ts: Date.now(),
|
||
author: L.author.trim() || "Anonymous",
|
||
title: L.title.trim(),
|
||
have: L.have.trim(),
|
||
want: L.want.trim(),
|
||
lane: L.lane,
|
||
body: L.body.trim(),
|
||
};
|
||
const userRows = loadBarter().filter((r) => !SEED_IDS.has(r.id));
|
||
saveBarter([row, ...userRows, ...seeds]);
|
||
return row;
|
||
}
|