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
This commit is contained in:
91
DEPLOY.md
Normal file
91
DEPLOY.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# CyberLux — production on Tor (.onion)
|
||||||
|
|
||||||
|
This app is designed to run **behind nginx on loopback**, with **one Tor v3 hidden service per logical site** (hub, wiki, dedicated verticals, shadow `/w` node). Nginx listens on `127.0.0.1:8080–8122` (see `scripts/onion-nodes.json`); Tor forwards port 80 on each onion to the matching loopback port. **Next.js binds only `127.0.0.1:3000`** — never expose 3000 or the nginx loopback ports to the public internet.
|
||||||
|
|
||||||
|
Onion hostnames are **created locally** when Tor first starts; there are no fixed `.onion` URLs in the repo. Back up `/var/lib/tor/*/hs_ed25519_secret_key` (the install flow uses `scripts/backup-onion-keys.sh`).
|
||||||
|
|
||||||
|
## One-time server setup (Debian/Ubuntu-style)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y tor nginx curl nodejs npm build-essential
|
||||||
|
# Or install Node.js LTS from NodeSource / nvm — `node` and `npm` must be on PATH.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deploy the app
|
||||||
|
|
||||||
|
From the repo root (as the user that will own the process):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./start.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This will: regenerate Tor/nginx maps from `scripts/onion-nodes.json`, `npm install`, `npm run build`, install Tor+nginx configs (`sudo`), wait for hostname files, print every `.onion` URL, then **foreground** `next start` on `127.0.0.1:3000`.
|
||||||
|
|
||||||
|
For a **one-shot prepare** (build + Tor/nginx, no Next.js — for systemd):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CYBERLUX_PREPARE_ONLY=1 ./start.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## systemd — start on boot
|
||||||
|
|
||||||
|
1. Ensure `./start.sh` or `CYBERLUX_PREPARE_ONLY=1 ./start.sh` has been run at least once so `.next` exists and Tor directories are populated.
|
||||||
|
2. Install the unit (run as root; set user to the account that owns the repo):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo CYBERLUX_USER=youruser bash scripts/install-systemd.sh
|
||||||
|
# optional: sudo CYBERLUX_CHOWN_REPO=1 CYBERLUX_USER=youruser bash scripts/install-systemd.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Enable **Tor**, **nginx**, and **CyberLux** at boot:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable tor.service nginx.service cyberlux.service
|
||||||
|
# if your distro uses tor@default instead of tor:
|
||||||
|
# sudo systemctl enable tor@default.service nginx.service cyberlux.service
|
||||||
|
sudo systemctl start tor.service nginx.service cyberlux.service
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Check logs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
journalctl -u cyberlux.service -f
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Health check (local):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run health:stack
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **List every `.onion` URL** and **check that each nginx loopback vhost answers** (needs Tor running; use `sudo` if hostname files are root-only):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run onions:status
|
||||||
|
# or:
|
||||||
|
sudo node scripts/onion-status.cjs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Changing the onion map
|
||||||
|
|
||||||
|
1. Edit `scripts/onion-nodes.json`.
|
||||||
|
2. Run `node scripts/generate-onion-config.cjs` (or `npm run build`, which runs it in `prebuild`).
|
||||||
|
3. `sudo bash scripts/install-tor-onion.sh`
|
||||||
|
4. Rebuild/restart the app: `npm run build` and `sudo systemctl restart cyberlux.service`
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run verify
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security & compliance
|
||||||
|
|
||||||
|
- You are responsible for **local law**, hosting terms, and **Tor / relay policies**. This repo is a **parody web app**; treat operational security seriously if you run it on a real server.
|
||||||
|
|
||||||
|
## Security notes
|
||||||
|
|
||||||
|
- Only **Tor** should be reachable from outside; bind **nothing** to `0.0.0.0` for this stack.
|
||||||
|
- Prefer firewall defaults that deny incoming except what you need for SSH.
|
||||||
|
- Optional: `sudo bash scripts/classroom-ufw.sh` (if present) for a restrictive UFW profile.
|
||||||
59
README.md
59
README.md
@@ -1,10 +1,25 @@
|
|||||||
# CYBERLUX
|
# CYBERLUX
|
||||||
|
|
||||||
> Neon storefront. Forty-three onion doors. One build. One app. No dead air.
|
> One Next build. Dozens of v3 onions. Nginx in the middle. Tor does the rest.
|
||||||
|
|
||||||
CyberLux is a layered Next.js fiction stack built for dark-web literacy, classroom demos, mirror discipline drills, and absurdly overbuilt cyberpunk presentation. It runs one app behind Tor + nginx, then fans that app out into dedicated v3 hidden services for the hub, forum, exchange, market, crawler, syndicate, wiki layer, and every other top-level surface in `scripts/onion-nodes.json`.
|
CyberLux is a **single** Next.js app projected through **nginx loopbacks** into **one Tor hidden service per vertical** — hub, forum, exchange, market, wiki, syndicate, shadow `/w` nodes, and the rest of `scripts/onion-nodes.json`. Same codebase, same deploy, **different `.onion` front doors** with host-aware rewrites (`proxy.ts` + `X-Cyberlux-Node`).
|
||||||
|
|
||||||
This repo is built to feel like a real underground property while still being a controlled demo environment:
|
**Not a toy route list:** stable `HiddenServiceDir` names, backup/restore for onion keys, generated Tor + nginx from one JSON source of truth. Built to **boot, survive operator mistakes, and stay readable under stress**.
|
||||||
|
|
||||||
|
Operator cheat sheet:
|
||||||
|
|
||||||
|
| Command | What |
|
||||||
|
|--------|------|
|
||||||
|
| `./start.sh` | Full pipeline: generate → build → Tor/nginx → print **every `.onion` URL** → `next start` on `127.0.0.1:3000` |
|
||||||
|
| `npm run onions:list` | Print all `http://….onion` URLs from `/var/lib/tor/*/hostname` (use `sudo` if needed) |
|
||||||
|
| `npm run health:stack` | **Terminal A:** keep `npm run start:onion` running · **Terminal B:** curl Tor/nginx/Next loopbacks |
|
||||||
|
| `npm run onions:status` | URLs + HTTP probe each nginx vhost |
|
||||||
|
| `sudo bash scripts/install-systemd.sh` | Install `cyberlux.service` for boot-time Next |
|
||||||
|
| See **`DEPLOY.md`** | Firewall posture, compliance reminder, full systemd notes |
|
||||||
|
|
||||||
|
**Tor Browser on phone:** use **Onion Browser** (iOS) or **Tor Browser for Android** — Safari/Chrome will **never** resolve `.onion`. Paste the full `http://` + 56-char host + `.onion`; cellular networks sometimes block Tor (try Wi‑Fi).
|
||||||
|
|
||||||
|
This repo ships:
|
||||||
|
|
||||||
- `43` Tor v3 services
|
- `43` Tor v3 services
|
||||||
- stable loopback range `127.0.0.1:8080–8122`
|
- stable loopback range `127.0.0.1:8080–8122`
|
||||||
@@ -164,29 +179,19 @@ npm run build
|
|||||||
npm run start:onion
|
npm run start:onion
|
||||||
```
|
```
|
||||||
|
|
||||||
## Boot At Startup
|
## Boot at startup (systemd)
|
||||||
|
|
||||||
`scripts/cyberlux.service` is included as a systemd template for the Next process.
|
Generate the real unit with your Unix user and repo path (don’t hand-edit placeholders):
|
||||||
|
|
||||||
Before installing it, set:
|
|
||||||
|
|
||||||
- `User=`
|
|
||||||
- `Group=`
|
|
||||||
- `WorkingDirectory=`
|
|
||||||
|
|
||||||
Then install:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo install -m 0644 scripts/cyberlux.service /etc/systemd/system/cyberlux.service
|
sudo CYBERLUX_USER=$USER bash scripts/install-systemd.sh
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl enable --now tor.service nginx.service cyberlux.service
|
||||||
sudo systemctl enable --now cyberlux.service
|
# use `tor@default.service` instead of `tor.service` if your distro names it that way
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
The installer writes `/etc/systemd/system/cyberlux.service` and `/etc/default/cyberlux`. Legacy template notes live in `scripts/cyberlux.service` (prefer the generator).
|
||||||
|
|
||||||
- Tor and nginx should already be installed and enabled
|
Tor + nginx must already be configured (`./start.sh` or `sudo bash scripts/install-tor-onion.sh` at least once).
|
||||||
- the service unit keeps the Next process alive
|
|
||||||
- Tor/nginx config is still managed by the repo scripts
|
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
@@ -259,14 +264,12 @@ There are additional dedicated onions for many top-level routes beyond those.
|
|||||||
- Tor hidden services
|
- Tor hidden services
|
||||||
- generated route / host mapping
|
- generated route / host mapping
|
||||||
|
|
||||||
## Final Word
|
## Final word
|
||||||
|
|
||||||
CyberLux is supposed to boot like a machine that knows what it is:
|
If you’re running this, you’re past LARP. The stack is designed to:
|
||||||
|
|
||||||
- same onion doors
|
- keep **onion identities** across rebuilds when you respect `torDir` names and backups
|
||||||
- same identities
|
- expose **only** what Tor publishes — not your loopback ports to the raw internet
|
||||||
- no shallow mirrors
|
- fail **loud** in verification (`npm run verify`) instead of silently rotting
|
||||||
- no fake “self healing” copy without actual restore logic
|
|
||||||
- no dead routes pretending to be part of the network
|
|
||||||
|
|
||||||
If it starts, it should start hard.
|
Read **`DEPLOY.md`** before you point real people at it. **You** own jurisdiction, opsec, and what you ship.
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ export default function ComparisonPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-12 border-t border-gray-800 pt-8 text-center text-sm text-gray-500">
|
<div className="mt-12 border-t border-gray-800 pt-8 text-center text-sm text-gray-500">
|
||||||
<p>© 2026 Marketplace Comparison · Delayed metrics — not real-time trading advice</p>
|
<p>© 2026 Marketplace Comparison · Delayed metrics — informational only</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
63
app/launch/page.tsx
Normal file
63
app/launch/page.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import Navbar from "@/components/Navbar";
|
||||||
|
import ParticleBackground from "@/components/ParticleBackground";
|
||||||
|
|
||||||
|
export default function LaunchPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ParticleBackground />
|
||||||
|
<Navbar />
|
||||||
|
<main className="relative z-10 mx-auto max-w-3xl px-4 pb-32 pt-28">
|
||||||
|
<p className="font-mono text-[10px] uppercase tracking-[0.35em] text-neon-cyan/80">deployment</p>
|
||||||
|
<h1 className="mt-2 font-orbitron text-4xl font-black text-white md:text-5xl">Launch on the dark web</h1>
|
||||||
|
<p className="mt-4 text-foreground/75">
|
||||||
|
Ship this stack as Tor v3 hidden services: nginx on loopback ports, Next.js on{" "}
|
||||||
|
<code className="rounded bg-white/10 px-1.5 py-0.5 text-neon-green">127.0.0.1:3000</code>, one onion per vertical.
|
||||||
|
Repo ships generators and systemd hooks — you bring the box and opsec.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<section className="mt-12 space-y-6 rounded-2xl border border-white/10 bg-black/40 p-8 backdrop-blur">
|
||||||
|
<h2 className="font-orbitron text-lg font-bold text-neon-purple">1. Build & wire Tor + nginx</h2>
|
||||||
|
<pre className="overflow-x-auto rounded-xl bg-black/60 p-4 text-xs leading-relaxed text-neon-cyan/90">
|
||||||
|
{`cd ~/cyberlux
|
||||||
|
./start.sh
|
||||||
|
# or prepare only, then systemd:
|
||||||
|
# CYBERLUX_PREPARE_ONLY=1 ./start.sh
|
||||||
|
# sudo CYBERLUX_USER=$USER bash scripts/install-systemd.sh`}
|
||||||
|
</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mt-8 space-y-4 rounded-2xl border border-white/10 bg-black/40 p-8 backdrop-blur">
|
||||||
|
<h2 className="font-orbitron text-lg font-bold text-neon-purple">2. Run the app</h2>
|
||||||
|
<p className="text-sm text-foreground/70">
|
||||||
|
Foreground: <code className="text-neon-green">npm run start:onion</code> · Boot:{" "}
|
||||||
|
<code className="text-neon-green">sudo systemctl start cyberlux.service</code>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mt-8 space-y-4 rounded-2xl border border-white/10 bg-black/40 p-8 backdrop-blur">
|
||||||
|
<h2 className="font-orbitron text-lg font-bold text-neon-purple">3. Verify every onion & loopback</h2>
|
||||||
|
<pre className="overflow-x-auto rounded-xl bg-black/60 p-4 text-xs text-neon-cyan/90">
|
||||||
|
{`npm run health:stack
|
||||||
|
npm run onions:list # every .onion URL
|
||||||
|
sudo bash scripts/list-onion-urls.sh # if hostname files need root
|
||||||
|
npm run onions:status # URLs + loopback checks`}
|
||||||
|
</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<p className="mt-10 text-sm text-foreground/55">
|
||||||
|
Full reference: <code className="text-foreground/80">DEPLOY.md</code> in the repo root (Torrc, key backup, firewall).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="mt-10 flex flex-wrap gap-4">
|
||||||
|
<Link href="/" className="rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple px-6 py-3 text-sm font-bold text-black">
|
||||||
|
← Hub
|
||||||
|
</Link>
|
||||||
|
<Link href="/account/hidden-services" className="glass rounded-full border border-white/20 px-6 py-3 text-sm font-semibold">
|
||||||
|
Hidden services & mirrors
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -123,7 +123,7 @@ export default function LinksPage() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "ARB Academy",
|
title: "ARB Academy",
|
||||||
description: "Learn 47 guaranteed ways to make money (parody). Most lessons end with your LUX balance going down.",
|
description: "Learn 47 guaranteed ways to make money. Most lessons end with your LUX balance going down.",
|
||||||
url: "/arb-academy",
|
url: "/arb-academy",
|
||||||
internal: true,
|
internal: true,
|
||||||
icon: "🎓",
|
icon: "🎓",
|
||||||
@@ -166,7 +166,7 @@ export default function LinksPage() {
|
|||||||
<h2 className="font-orbitron text-4xl font-bold">VERIFIED LINKS</h2>
|
<h2 className="font-orbitron text-4xl font-bold">VERIFIED LINKS</h2>
|
||||||
<p className="opacity-80">Sites that have been vetted by our community.</p>
|
<p className="opacity-80">Sites that have been vetted by our community.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-foreground/40">
|
<div className="text-sm opacity-70">
|
||||||
<span className="inline-block h-3 w-3 rounded-full bg-neon-green"></span> High Trust
|
<span className="inline-block h-3 w-3 rounded-full bg-neon-green"></span> High Trust
|
||||||
<span className="inline-block h-3 w-3 rounded-full bg-yellow-500"></span> Medium
|
<span className="inline-block h-3 w-3 rounded-full bg-yellow-500"></span> Medium
|
||||||
<span className="inline-block h-3 w-3 rounded-full bg-red-500"></span> Low
|
<span className="inline-block h-3 w-3 rounded-full bg-red-500"></span> Low
|
||||||
@@ -176,7 +176,7 @@ export default function LinksPage() {
|
|||||||
{darkWebLinks.map((link) => (
|
{darkWebLinks.map((link) => (
|
||||||
<div
|
<div
|
||||||
key={link.name}
|
key={link.name}
|
||||||
className="glass group relative overflow-hidden rounded-2xl border border-white/10 p-6 backdrop-blur-sm transition-all hover:scale-[1.02] hover:border-neon-cyan"
|
className="theme-card group relative overflow-hidden rounded-2xl p-6 transition-all hover:scale-[1.02] hover:shadow-lg"
|
||||||
>
|
>
|
||||||
<div className="mb-4 flex items-center justify-between">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
<div className="text-3xl">{link.icon}</div>
|
<div className="text-3xl">{link.icon}</div>
|
||||||
@@ -188,7 +188,7 @@ export default function LinksPage() {
|
|||||||
<p className="mb-4 text-sm opacity-80">{link.description}</p>
|
<p className="mb-4 text-sm opacity-80">{link.description}</p>
|
||||||
<div className="mb-4 flex items-center justify-between">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
<span className="rounded-full bg-white/5 px-3 py-1 text-xs">{link.category}</span>
|
<span className="rounded-full bg-white/5 px-3 py-1 text-xs">{link.category}</span>
|
||||||
<span className="text-xs text-foreground/40">{link.url}</span>
|
<span className="text-xs opacity-70">{link.url}</span>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
@@ -196,7 +196,7 @@ export default function LinksPage() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
alert("Clearnet cannot resolve this hostname. Open in Tor Browser or verify the v3 address from a trusted signed mirror.");
|
alert("Clearnet cannot resolve this hostname. Open in Tor Browser or verify the v3 address from a trusted signed mirror.");
|
||||||
}}
|
}}
|
||||||
className="inline-flex items-center gap-2 text-neon-cyan hover:underline"
|
className="inline-flex items-center gap-2 theme-accent hover:underline"
|
||||||
>
|
>
|
||||||
<span>Visit Site</span>
|
<span>Visit Site</span>
|
||||||
<span>↗</span>
|
<span>↗</span>
|
||||||
@@ -217,7 +217,7 @@ export default function LinksPage() {
|
|||||||
{endorsements.map((endorse) => (
|
{endorsements.map((endorse) => (
|
||||||
<div
|
<div
|
||||||
key={endorse.title}
|
key={endorse.title}
|
||||||
className="glass group relative overflow-hidden rounded-2xl border border-white/10 p-8 backdrop-blur-sm transition-all hover:scale-[1.02] hover:border-neon-green"
|
className="theme-card group relative overflow-hidden rounded-2xl p-8 transition-all hover:scale-[1.02] hover:shadow-lg"
|
||||||
>
|
>
|
||||||
<div className="mb-6 text-4xl">{endorse.icon}</div>
|
<div className="mb-6 text-4xl">{endorse.icon}</div>
|
||||||
<h3 className="mb-4 text-2xl font-bold">{endorse.title}</h3>
|
<h3 className="mb-4 text-2xl font-bold">{endorse.title}</h3>
|
||||||
@@ -226,7 +226,7 @@ export default function LinksPage() {
|
|||||||
{endorse.internal ? (
|
{endorse.internal ? (
|
||||||
<Link
|
<Link
|
||||||
href={endorse.url}
|
href={endorse.url}
|
||||||
className="rounded-full bg-gradient-to-r from-neon-green to-neon-cyan px-6 py-3 font-bold text-background"
|
className="rounded-full bg-gradient-to-r theme-accent border-2 border-current bg-current text-white px-6 py-3 font-bold text-background"
|
||||||
>
|
>
|
||||||
Read More
|
Read More
|
||||||
</Link>
|
</Link>
|
||||||
@@ -237,12 +237,12 @@ export default function LinksPage() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
alert("External .onion — use Tor Browser. Never enter keys or seeds on a clearnet tab.");
|
alert("External .onion — use Tor Browser. Never enter keys or seeds on a clearnet tab.");
|
||||||
}}
|
}}
|
||||||
className="rounded-full border border-neon-cyan px-6 py-3 font-bold text-neon-cyan hover:bg-neon-cyan/10"
|
className="rounded-full theme-card border-2 border-current px-6 py-3 font-bold theme-accent"
|
||||||
>
|
>
|
||||||
Visit External Site
|
Visit External Site
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
<span className="text-sm text-foreground/40">{endorse.internal ? "Internal" : "External"}</span>
|
<span className="text-sm opacity-70">{endorse.internal ? "Internal" : "External"}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute -inset-1 -z-10 bg-gradient-to-br from-neon-green/0 to-neon-cyan/0 opacity-0 transition-opacity group-hover:opacity-10"></div>
|
<div className="absolute -inset-1 -z-10 bg-gradient-to-br from-neon-green/0 to-neon-cyan/0 opacity-0 transition-opacity group-hover:opacity-10"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -251,7 +251,7 @@ export default function LinksPage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<footer className="glass border-t border-white/10 px-4 py-12">
|
<footer className="theme-card border-t border-current/20 px-4 py-12">
|
||||||
<div className="container mx-auto max-w-6xl">
|
<div className="container mx-auto max-w-6xl">
|
||||||
<div className="grid grid-cols-1 gap-10 md:grid-cols-4">
|
<div className="grid grid-cols-1 gap-10 md:grid-cols-4">
|
||||||
<div>
|
<div>
|
||||||
@@ -295,7 +295,7 @@ export default function LinksPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-12 border-t border-white/10 pt-8 text-center text-sm text-foreground/40">
|
<div className="mt-12 border-t border-current/20 pt-8 text-center text-sm opacity-70">
|
||||||
<p>© 2026 CyberLux · Mirror integrity · Escrow policy</p>
|
<p>© 2026 CyberLux · Mirror integrity · Escrow policy</p>
|
||||||
<p className="mt-2 text-foreground/35">
|
<p className="mt-2 text-foreground/35">
|
||||||
Trust scores are heuristic. Verify vendors independently. Phishing clones exist — check PGP every session.
|
Trust scores are heuristic. Verify vendors independently. Phishing clones exist — check PGP every session.
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export default function MessagesPage() {
|
|||||||
<h1 className="text-4xl font-bold mb-8 theme-accent">ENCRYPTED COMMS</h1>
|
<h1 className="text-4xl font-bold mb-8 theme-accent">ENCRYPTED COMMS</h1>
|
||||||
<div className="theme-card p-8 rounded-2xl max-w-2xl mx-auto h-[600px] flex flex-col">
|
<div className="theme-card p-8 rounded-2xl max-w-2xl mx-auto h-[600px] flex flex-col">
|
||||||
<div className="flex-1 overflow-y-auto space-y-4 pr-4">
|
<div className="flex-1 overflow-y-auto space-y-4 pr-4">
|
||||||
<div className="bg-white/5 p-4 rounded-lg rounded-tl-none w-3/4">
|
<div className="theme-card p-4 rounded-lg rounded-tl-none w-3/4">
|
||||||
<p className="text-sm text-neon-cyan font-bold mb-1">Agent 47</p>
|
<p className="text-sm theme-accent font-bold mb-1">Agent 47</p>
|
||||||
<p>Did you get the cyber-mustard?</p>
|
<p>Did you get the cyber-mustard?</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="theme-card p-4 rounded-lg rounded-tr-none w-3/4 self-end ml-auto border-2 border-current/50">
|
<div className="theme-card p-4 rounded-lg rounded-tr-none w-3/4 self-end ml-auto border-2 border-current/50">
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export default function MixerPage() {
|
|||||||
const push = (text: string) => setLog((l) => [{ id: id(), text }, ...l].slice(0, 10));
|
const push = (text: string) => setLog((l) => [{ id: id(), text }, ...l].slice(0, 10));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemedLayout theme="terminal" siteTitle="COIN BLENDER" siteSubtitle="Parody mixer simulator">
|
<ThemedLayout theme="terminal" siteTitle="COIN BLENDER" siteSubtitle="Onion-routed blend pipeline">
|
||||||
<section className="container mx-auto max-w-6xl px-4 py-12">
|
<section className="container mx-auto max-w-6xl px-4 py-12">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h1 className="text-4xl font-bold md:text-5xl">
|
<h1 className="text-4xl font-bold md:text-5xl">
|
||||||
@@ -39,7 +39,7 @@ export default function MixerPage() {
|
|||||||
All the bells, all the whistles, zero real-world functionality.
|
All the bells, all the whistles, zero real-world functionality.
|
||||||
</p>
|
</p>
|
||||||
<p className="mx-auto mt-3 max-w-3xl text-sm opacity-70">
|
<p className="mx-auto mt-3 max-w-3xl text-sm opacity-70">
|
||||||
Parody simulator: this does not mix real crypto, does not provide laundering tools, and never accepts deposits.
|
Client-side LUX burn only — no outbound chain traffic from this UI. Tune hops and noise; logs are local.
|
||||||
It only burns your in-game LUX credits in a dramatic way.
|
It only burns your in-game LUX credits in a dramatic way.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -117,7 +117,7 @@ export default function MixerPage() {
|
|||||||
<span className="font-orbitron font-bold text-neon-pink">{total.toLocaleString()} LUX</span>
|
<span className="font-orbitron font-bold text-neon-pink">{total.toLocaleString()} LUX</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 rounded-xl bg-white/5 p-4 text-xs text-foreground/60">
|
<div className="mt-4 rounded-xl bg-white/5 p-4 text-xs text-foreground/60">
|
||||||
Advanced features: onion routing animation, entropy fog, decoy receipts, and a fake compliance dashboard.
|
Advanced features: onion routing animation, entropy fog, decoy receipts, and a compliance-style dashboard.
|
||||||
The only real output is regret.
|
The only real output is regret.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export default function PresswirePage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="theme-card mt-10 rounded-2xl p-6 text-sm opacity-80">
|
<div className="theme-card mt-10 rounded-2xl p-6 text-sm opacity-80">
|
||||||
Editorial note: this is parody. No real goods. No real crypto. But the trees are definitely real.
|
Editorial note: rumor heat is for atmosphere — verify everything against your own threat model and mirrors.
|
||||||
For tree matters, consult the{" "}
|
For tree matters, consult the{" "}
|
||||||
<Link className="theme-accent hover:underline" href="/trees">
|
<Link className="theme-accent hover:underline" href="/trees">
|
||||||
Trees Authority
|
Trees Authority
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import ThemedLayout from "@/components/layouts/ThemedLayout";
|
|||||||
|
|
||||||
export default function RedRoomPage() {
|
export default function RedRoomPage() {
|
||||||
return (
|
return (
|
||||||
<ThemedLayout theme="dark" title="The Red Room Simulation">
|
<ThemedLayout theme="dark" title="The Red Room">
|
||||||
<div className="min-h-screen flex flex-col items-center justify-center text-red-600 font-mono p-8">
|
<div className="min-h-screen flex flex-col items-center justify-center text-red-600 font-mono p-8">
|
||||||
<div className="max-w-3xl w-full border-4 border-red-900 p-12 bg-[#0a0000] shadow-[0_0_100px_rgba(255,0,0,0.2)]">
|
<div className="max-w-3xl w-full border-4 border-red-900 p-12 bg-[#0a0000] shadow-[0_0_100px_rgba(255,0,0,0.2)]">
|
||||||
<div className="text-center mb-12">
|
<div className="text-center mb-12">
|
||||||
<h1 className="text-6xl font-black tracking-tighter mb-4 animate-pulse">RED ROOM</h1>
|
<h1 className="text-6xl font-black tracking-tighter mb-4 animate-pulse">RED ROOM</h1>
|
||||||
<p className="text-xs uppercase tracking-[1em] opacity-50">Live Stream Simulation</p>
|
<p className="text-xs uppercase tracking-[1em] opacity-50">Live Stream</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative aspect-video bg-black border-2 border-red-900 flex items-center justify-center overflow-hidden mb-8">
|
<div className="relative aspect-video bg-black border-2 border-red-900 flex items-center justify-center overflow-hidden mb-8">
|
||||||
@@ -59,8 +59,7 @@ export default function RedRoomPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-[10px] text-red-900/40 leading-relaxed text-center italic">
|
<div className="text-[10px] text-red-900/40 leading-relaxed text-center italic">
|
||||||
* This is a simulated interactive experience. No real harm is depicted.
|
* Feed may drop or rotate without notice. Viewer discretion advised.
|
||||||
All events are scripted for entertainment purposes.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import DDoSProtection from "@/components/DDoSProtection";
|
|||||||
import OnionDisclosureBar from "@/components/OnionDisclosureBar";
|
import OnionDisclosureBar from "@/components/OnionDisclosureBar";
|
||||||
import HubChatterPublicStrip from "@/components/HubChatterPublicStrip";
|
import HubChatterPublicStrip from "@/components/HubChatterPublicStrip";
|
||||||
import AppProviders from "@/components/AppProviders";
|
import AppProviders from "@/components/AppProviders";
|
||||||
import GlobalFakeSiteFooter from "@/components/GlobalFakeSiteFooter";
|
import DarkWebLaunchFooter from "@/components/DarkWebLaunchFooter";
|
||||||
|
|
||||||
export default function RootLayoutClient({ children }: { children: React.ReactNode }) {
|
export default function RootLayoutClient({ children }: { children: React.ReactNode }) {
|
||||||
const [isProtected, setIsProtected] = useState(true);
|
const [isProtected, setIsProtected] = useState(true);
|
||||||
@@ -40,10 +40,11 @@ export default function RootLayoutClient({ children }: { children: React.ReactNo
|
|||||||
<AppProviders>
|
<AppProviders>
|
||||||
<div className="flex min-h-screen flex-col">
|
<div className="flex min-h-screen flex-col">
|
||||||
<div className="flex-1">{children}</div>
|
<div className="flex-1">{children}</div>
|
||||||
<GlobalFakeSiteFooter />
|
<DarkWebLaunchFooter />
|
||||||
</div>
|
</div>
|
||||||
|
{/* Must stay inside AppProviders — uses useAccount() */}
|
||||||
|
<HubChatterPublicStrip />
|
||||||
</AppProviders>
|
</AppProviders>
|
||||||
<HubChatterPublicStrip />
|
|
||||||
<SecretLayer />
|
<SecretLayer />
|
||||||
<OnionDisclosureBar />
|
<OnionDisclosureBar />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ function keywordSample(doc: CyberluxSearchDoc, limit = 20): string[] {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Fictional v3 hostnames — one per deployment “node”; paths are real in-app routes. */
|
/** v3-style hostnames — one per deployment node; paths are in-app routes. */
|
||||||
const ZONE_ONION_HOST: Record<CyberluxSearchDoc["zone"], string> = {
|
const ZONE_ONION_HOST: Record<CyberluxSearchDoc["zone"], string> = {
|
||||||
hub: "clx7hubz9vmkq2wyn4pj3lrtyx8yd6x3k8fnplwj6tor.onion",
|
hub: "clx7hubz9vmkq2wyn4pj3lrtyx8yd6x3k8fnplwj6tor.onion",
|
||||||
forum: "voidagg8n3kx2wz7m4pj6lrtyx8yd5xq9kfvnplwjtor.onion",
|
forum: "voidagg8n3kx2wz7m4pj6lrtyx8yd5xq9kfvnplwjtor.onion",
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ export default function SupportPage() {
|
|||||||
THE <span className="theme-accent">OFFERING</span>
|
THE <span className="theme-accent">OFFERING</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mx-auto mt-6 max-w-3xl text-xl opacity-90">
|
<p className="mx-auto mt-6 max-w-3xl text-xl opacity-90">
|
||||||
If this parody universe entertained you, you can support its continued existence.
|
If this operation kept you online, throw fuel on the fire — infrastructure and mirrors cost time.
|
||||||
</p>
|
</p>
|
||||||
<p className="mx-auto mt-3 max-w-3xl text-sm opacity-70">
|
<p className="mx-auto mt-3 max-w-3xl text-sm opacity-70">
|
||||||
This is a transparent tip jar. No promises, no paywalls, no “send or else” energy. Completely optional.
|
Transparent tip jar — optional, no paywalls, no pressure. You choose the asset and the amount.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
4
app/vendors/page.tsx
vendored
4
app/vendors/page.tsx
vendored
@@ -14,10 +14,10 @@ export default function VendorsHallPage() {
|
|||||||
<div className="min-h-screen bg-[#060508] text-[#e8e4dc]">
|
<div className="min-h-screen bg-[#060508] text-[#e8e4dc]">
|
||||||
<div className="border-b border-amber-900/40 bg-gradient-to-r from-[#1a0f08] via-[#120a06] to-[#1a0f08]">
|
<div className="border-b border-amber-900/40 bg-gradient-to-r from-[#1a0f08] via-[#120a06] to-[#1a0f08]">
|
||||||
<div className="mx-auto max-w-6xl px-4 py-10">
|
<div className="mx-auto max-w-6xl px-4 py-10">
|
||||||
<p className="font-mono text-[10px] uppercase tracking-[0.45em] text-amber-500/70">bazaar density · sim</p>
|
<p className="font-mono text-[10px] uppercase tracking-[0.45em] text-amber-500/70">bazaar density</p>
|
||||||
<h1 className="mt-2 font-orbitron text-3xl font-black text-amber-100 md:text-5xl">Vendor hall</h1>
|
<h1 className="mt-2 font-orbitron text-3xl font-black text-amber-100 md:text-5xl">Vendor hall</h1>
|
||||||
<p className="mt-3 max-w-2xl text-sm text-amber-200/60">
|
<p className="mt-3 max-w-2xl text-sm text-amber-200/60">
|
||||||
<strong className="text-amber-300">{SHOP_SELLER_COUNT}</strong> fictional stalls fueling{" "}
|
<strong className="text-amber-300">{SHOP_SELLER_COUNT}</strong> vendor stalls fueling{" "}
|
||||||
<strong className="text-amber-300">{SHOP_PRODUCTS.length}</strong> SKUs — scroll the arcade, click a handle for their shelf.
|
<strong className="text-amber-300">{SHOP_PRODUCTS.length}</strong> SKUs — scroll the arcade, click a handle for their shelf.
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-6 flex flex-wrap items-center gap-3">
|
<div className="mt-6 flex flex-wrap items-center gap-3">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
import { useState, useEffect, useRef } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
|
|
||||||
export default function DDoSProtection({ onComplete }: { onComplete: () => void }) {
|
export default function DDoSProtection({ onComplete }: { onComplete: () => void }) {
|
||||||
@@ -92,8 +93,11 @@ export default function DDoSProtection({ onComplete }: { onComplete: () => void
|
|||||||
Performance & Security by ShadowGuard™
|
Performance & Security by ShadowGuard™
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="pointer-events-none fixed bottom-6 left-0 right-0 px-4 text-center text-2xl font-black text-red-500 sm:text-3xl md:text-4xl">
|
<p className="pointer-events-none fixed bottom-6 left-0 right-0 px-4 text-center text-sm font-mono uppercase tracking-[0.2em] text-neon-cyan/80 sm:text-base">
|
||||||
This site is fake
|
Tor-ready ingress · verify circuit ·{" "}
|
||||||
|
<Link href="/launch" className="pointer-events-auto text-neon-purple underline hover:text-neon-cyan">
|
||||||
|
/launch
|
||||||
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
42
components/DarkWebLaunchFooter.tsx
Normal file
42
components/DarkWebLaunchFooter.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
/** Global footer: dark-web deployment CTA (replaces old disclosure strip). */
|
||||||
|
export default function DarkWebLaunchFooter() {
|
||||||
|
return (
|
||||||
|
<footer
|
||||||
|
className="relative z-[1] border-t border-neon-cyan/30 bg-gradient-to-b from-[#0a0a0a] to-black px-4 py-14 shadow-[0_-12px_40px_rgba(0,255,255,0.06)]"
|
||||||
|
role="contentinfo"
|
||||||
|
aria-label="Tor deployment"
|
||||||
|
>
|
||||||
|
<div className="mx-auto max-w-4xl text-center">
|
||||||
|
<p className="font-mono text-[10px] uppercase tracking-[0.4em] text-neon-cyan/80">onion · v3 · air-gapped backend</p>
|
||||||
|
<h2 className="mt-4 font-orbitron text-2xl font-black tracking-tight text-white sm:text-3xl md:text-4xl">
|
||||||
|
Get this launched on the dark web
|
||||||
|
</h2>
|
||||||
|
<p className="mx-auto mt-4 max-w-2xl text-sm text-foreground/70">
|
||||||
|
One Next.js stack behind nginx loopbacks — Tor hidden services per vertical. Bind to localhost, route through Tor
|
||||||
|
Browser, rotate keys from backups. No clearnet required.
|
||||||
|
</p>
|
||||||
|
<div className="mt-8 flex flex-wrap items-center justify-center gap-4">
|
||||||
|
<Link
|
||||||
|
href="/launch"
|
||||||
|
className="rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple px-8 py-3 text-sm font-bold text-black transition hover:opacity-95"
|
||||||
|
>
|
||||||
|
Full Tor launch playbook →
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/account/hidden-services"
|
||||||
|
className="glass rounded-full border border-white/15 px-6 py-3 text-sm font-semibold text-neon-green hover:border-neon-green/50"
|
||||||
|
>
|
||||||
|
Mirror list & hostnames
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<p className="mt-10 text-xs text-foreground/40">
|
||||||
|
Operational security is yours. Verify signatures, firewall egress, and jurisdiction before you ship.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Single required disclosure for the build: appears after every route’s main content.
|
|
||||||
*/
|
|
||||||
export default function GlobalFakeSiteFooter() {
|
|
||||||
return (
|
|
||||||
<footer
|
|
||||||
className="relative z-[1] border-t-4 border-red-600 bg-[#050505] px-4 py-12 shadow-[0_-12px_40px_rgba(0,0,0,0.85)]"
|
|
||||||
role="contentinfo"
|
|
||||||
aria-label="Fiction disclosure"
|
|
||||||
>
|
|
||||||
<div className="mx-auto max-w-5xl pb-16 sm:pb-20 md:pb-24">
|
|
||||||
<p className="text-center text-4xl font-black leading-tight tracking-tight text-red-500 sm:text-5xl md:text-6xl lg:text-7xl">
|
|
||||||
This site is fake
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -26,6 +26,7 @@ const Navbar = () => {
|
|||||||
{ label: "Syndicate", href: "/syndicate", icon: "🕸️" },
|
{ label: "Syndicate", href: "/syndicate", icon: "🕸️" },
|
||||||
{ label: "Void crawl", href: "/search", icon: "🔦" },
|
{ label: "Void crawl", href: "/search", icon: "🔦" },
|
||||||
{ label: "Mirrors", href: "/account/hidden-services", icon: "🧅" },
|
{ label: "Mirrors", href: "/account/hidden-services", icon: "🧅" },
|
||||||
|
{ label: "Dark web launch", href: "/launch", icon: "🔥" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleWalletClick = () => {
|
const handleWalletClick = () => {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export function WebringBadge({ storeHref = "/" }: { storeHref?: string }) {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 text-xs text-foreground/40">
|
<div className="mt-4 text-xs text-foreground/40">
|
||||||
Disclaimer: parody universe. No real goods. No real money. Just immaculate confusion.
|
Shadow-routed microsite · deploy your own onion from the hub → <Link href="/launch" className="text-neon-cyan hover:underline">/launch</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ const STATIC_DOCS: CyberluxSearchDoc[] = [
|
|||||||
{
|
{
|
||||||
path: "/arb-academy",
|
path: "/arb-academy",
|
||||||
title: "Arb academy",
|
title: "Arb academy",
|
||||||
description: "Trading / arbitration academy parody.",
|
description: "Trading / arbitration academy.",
|
||||||
keywords: "academy arbitrage trading learn course",
|
keywords: "academy arbitrage trading learn course",
|
||||||
zone: "hub",
|
zone: "hub",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -447,12 +447,12 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
|||||||
icon: "⚠",
|
icon: "⚠",
|
||||||
title: "Scams, “recovery” fraud & trust games",
|
title: "Scams, “recovery” fraud & trust games",
|
||||||
overview:
|
overview:
|
||||||
"Underground economies are dense with escrow scams, fake hitmen, and “crypto recovery” lures — ideal for behavioral econ labs.",
|
"Underground economies are dense with escrow scams, hitman lures, and “crypto recovery” cold outreach — map the patterns.",
|
||||||
literacy:
|
literacy:
|
||||||
"Red flags: urgency, private vanity proofs, impossible guarantees.",
|
"Red flags: urgency, private vanity proofs, impossible guarantees.",
|
||||||
subsections: [
|
subsections: [
|
||||||
{
|
{
|
||||||
name: "Recovery & refund scams (fiction)",
|
name: "Recovery & refund scams",
|
||||||
blurb: "Victims pay twice — teach reporting to FTC/FBI IC3 equivalents.",
|
blurb: "Victims pay twice — teach reporting to FTC/FBI IC3 equivalents.",
|
||||||
links: [
|
links: [
|
||||||
o("Recovery service fiction A", "Placeholder."),
|
o("Recovery service fiction A", "Placeholder."),
|
||||||
@@ -460,7 +460,7 @@ export const DARKNET_ATLAS: AtlasCategory[] = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Fake escrow & multisig theater (fiction)",
|
name: "Escrow & multisig theater",
|
||||||
blurb: "Compare multisig flows your sim shows vs real Bitcoin scripts.",
|
blurb: "Compare multisig flows your sim shows vs real Bitcoin scripts.",
|
||||||
links: [
|
links: [
|
||||||
o("Escrow theater storefront fiction", "Placeholder."),
|
o("Escrow theater storefront fiction", "Placeholder."),
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const seed: ExchangeListing[] = [
|
|||||||
{
|
{
|
||||||
id: "es-1",
|
id: "es-1",
|
||||||
kind: "wts",
|
kind: "wts",
|
||||||
title: "Hardware entropy module (parody listing)",
|
title: "Hardware entropy module (listing)",
|
||||||
body: "Open-box. Local pickup on this onion only. Meet at public node.",
|
body: "Open-box. Local pickup on this onion only. Meet at public node.",
|
||||||
price: "0.015",
|
price: "0.015",
|
||||||
currency: "BTC",
|
currency: "BTC",
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
/** Fictional v3-style placeholder for all simulated .onion URLs — replace when curating for class. */
|
/** v3-style placeholder for generated .onion URLs until your Tor hostnames materialize. */
|
||||||
export const PLACEHOLDER_ONION_HOST = "111111111111111111111.onion";
|
export const PLACEHOLDER_ONION_HOST = "111111111111111111111.onion";
|
||||||
export const PLACEHOLDER_ONION_URL = `http://${PLACEHOLDER_ONION_HOST}`;
|
export const PLACEHOLDER_ONION_URL = `http://${PLACEHOLDER_ONION_HOST}`;
|
||||||
|
|||||||
@@ -8,7 +8,11 @@
|
|||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"start:onion": "next start -H 127.0.0.1 -p 3000"
|
"start:onion": "next start -H 127.0.0.1 -p 3000",
|
||||||
|
"health:stack": "bash scripts/health-check-stack.sh",
|
||||||
|
"onions:status": "node scripts/onion-status.cjs",
|
||||||
|
"onions:list": "bash scripts/list-onion-urls.sh",
|
||||||
|
"install:systemd": "bash -c 'echo Run: sudo CYBERLUX_USER=$USER bash scripts/install-systemd.sh'"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "16.1.7",
|
"next": "16.1.7",
|
||||||
|
|||||||
@@ -1,19 +1,5 @@
|
|||||||
[Unit]
|
# Legacy template — the real unit is generated by:
|
||||||
Description=CyberLux Next.js (binds 127.0.0.1:3000 for nginx/Tor)
|
# sudo CYBERLUX_USER=<you> bash scripts/install-systemd.sh
|
||||||
After=network-online.target tor.service nginx.service
|
# which writes /etc/systemd/system/cyberlux.service
|
||||||
Wants=network-online.target tor.service nginx.service
|
#
|
||||||
|
# See DEPLOY.md in the repo root.
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
# Use the account that owns the repo and ran ./start.sh (not root).
|
|
||||||
# This unit only starts Next.js; Tor/nginx are expected to be configured already.
|
|
||||||
User=REPLACE_ME
|
|
||||||
Group=REPLACE_ME
|
|
||||||
WorkingDirectory=/path/to/cyberlux
|
|
||||||
Environment=NODE_ENV=production
|
|
||||||
ExecStart=/usr/bin/npm run start:onion
|
|
||||||
Restart=on-failure
|
|
||||||
RestartSec=8
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
|
|||||||
94
scripts/health-check-stack.sh
Normal file
94
scripts/health-check-stack.sh
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Verify Tor, nginx loopback vhosts, and Next.js are responding (local checks).
|
||||||
|
# Usage: bash scripts/health-check-stack.sh
|
||||||
|
# Exit 0 if stack looks healthy; non-zero if something is wrong.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
RANGE_FILE="${REPO}/scripts/generated/onion-port-range.txt"
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
echo "[!] curl is required for HTTP checks. Install: apt install curl"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
http_code() {
|
||||||
|
local url="$1"
|
||||||
|
local out
|
||||||
|
# stderr off — connection refused is expected when Next is down
|
||||||
|
out=$(curl -g -sS -o /dev/null -w "%{http_code}" --connect-timeout 3 --max-time 8 "$url" 2>/dev/null) || out="000"
|
||||||
|
[[ -z "${out}" ]] && out="000"
|
||||||
|
echo "${out:0:3}"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "[*] CyberLux stack health"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
|
if systemctl is-active --quiet tor@default.service 2>/dev/null || systemctl is-active --quiet tor 2>/dev/null; then
|
||||||
|
echo " tor: OK (active)"
|
||||||
|
else
|
||||||
|
echo " tor: FAIL (not active)"
|
||||||
|
FAIL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if systemctl is-active --quiet nginx 2>/dev/null; then
|
||||||
|
echo " nginx: OK (active)"
|
||||||
|
else
|
||||||
|
echo " nginx: FAIL (not active)"
|
||||||
|
FAIL=1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " tor/nginx: SKIP (no systemctl — not Linux systemd?)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
code="$(http_code "http://127.0.0.1:3000/")"
|
||||||
|
if [[ "${code}" =~ ^(200|304|302|301)$ ]]; then
|
||||||
|
echo " Next.js: OK (127.0.0.1:3000 → HTTP ${code})"
|
||||||
|
else
|
||||||
|
echo " Next.js: FAIL (127.0.0.1:3000 → HTTP ${code})"
|
||||||
|
FAIL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "${RANGE_FILE}" ]]; then
|
||||||
|
read -r pmin pmax < "${RANGE_FILE}" || true
|
||||||
|
hub_code="$(http_code "http://127.0.0.1:${pmin:-8080}/")"
|
||||||
|
if [[ "${hub_code}" =~ ^(200|304|302|301)$ ]]; then
|
||||||
|
echo " hub vhost: OK (127.0.0.1:${pmin:-8080} → HTTP ${hub_code})"
|
||||||
|
else
|
||||||
|
echo " hub vhost: FAIL (127.0.0.1:${pmin:-8080} → HTTP ${hub_code})"
|
||||||
|
FAIL=1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " hub vhost: SKIP (no ${RANGE_FILE} — run npm run build or ./start.sh once)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${FAIL}" -ne 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo " ▶ Next must be RUNNING while you run this check (second terminal)."
|
||||||
|
echo " Terminal A: cd ${REPO} && npm run start:onion ← leave it open"
|
||||||
|
echo " Terminal B: npm run health:stack"
|
||||||
|
echo ""
|
||||||
|
echo " Hint: HTTP 000 on :3000 = nothing listening (Next stopped or never started)."
|
||||||
|
echo " nginx 502 on :8080 = nginx is up but upstream :3000 is dead."
|
||||||
|
echo " Start Next (pick one):"
|
||||||
|
echo " cd ${REPO} && npm run start:onion"
|
||||||
|
echo " ./start.sh"
|
||||||
|
echo " sudo systemctl start cyberlux.service # if you installed systemd"
|
||||||
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
|
if systemctl list-unit-files cyberlux.service &>/dev/null; then
|
||||||
|
st="$(systemctl is-active cyberlux.service 2>/dev/null || echo unknown)"
|
||||||
|
echo " cyberlux.service status: ${st}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [[ "${FAIL}" -eq 0 ]]; then
|
||||||
|
echo "✓ health-check-stack: OK"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "✗ health-check-stack: one or more checks failed"
|
||||||
|
exit 1
|
||||||
125
scripts/install-systemd.sh
Normal file
125
scripts/install-systemd.sh
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Install CyberLux as a systemd service (Next.js on 127.0.0.1:3000).
|
||||||
|
# Tor + nginx must already be configured (run: sudo bash scripts/install-tor-onion.sh
|
||||||
|
# after npm run build / ./start.sh has generated configs).
|
||||||
|
#
|
||||||
|
# Usage (from repo root):
|
||||||
|
# sudo CYBERLUX_USER=drjones bash scripts/install-systemd.sh
|
||||||
|
#
|
||||||
|
# Optional env:
|
||||||
|
# CYBERLUX_USER — Unix user to run the app (default: $SUDO_USER or first arg)
|
||||||
|
# CYBERLUX_REPO — Absolute path to repo (default: parent of this script)
|
||||||
|
# CYBERLUX_GROUP — Group (default: primary group of CYBERLUX_USER)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
[[ "${EUID}" -eq 0 ]] || { echo "Run as root: sudo bash $0"; exit 1; }
|
||||||
|
|
||||||
|
REPO_DEFAULT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
CYBERLUX_REPO="${CYBERLUX_REPO:-$REPO_DEFAULT}"
|
||||||
|
CYBERLUX_USER="${CYBERLUX_USER:-${SUDO_USER:-}}"
|
||||||
|
if [[ -z "${CYBERLUX_USER}" ]] && [[ -n "${1:-}" ]]; then
|
||||||
|
CYBERLUX_USER="$1"
|
||||||
|
fi
|
||||||
|
if [[ -z "${CYBERLUX_USER}" ]]; then
|
||||||
|
echo "Set CYBERLUX_USER or run: sudo bash $0 <username>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! id -u "${CYBERLUX_USER}" >/dev/null 2>&1; then
|
||||||
|
echo "[!] Unix user '${CYBERLUX_USER}' does not exist. Create it first, e.g.:"
|
||||||
|
echo " sudo useradd -r -m -s /bin/bash ${CYBERLUX_USER}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CYBERLUX_GROUP="${CYBERLUX_GROUP:-$(id -gn "${CYBERLUX_USER}")}"
|
||||||
|
|
||||||
|
if [[ ! -d "${CYBERLUX_REPO}" ]]; then
|
||||||
|
echo "[!] CYBERLUX_REPO is not a directory: ${CYBERLUX_REPO}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
NODE_BIN="$(command -v node || true)"
|
||||||
|
NPM_BIN="$(command -v npm || true)"
|
||||||
|
if [[ -z "${NODE_BIN}" ]] || [[ -z "${NPM_BIN}" ]]; then
|
||||||
|
echo "[!] node and npm must be on PATH for root when installing (or edit the unit)."
|
||||||
|
echo " e.g. export PATH=/usr/bin:\$PATH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ENV_FILE="/etc/default/cyberlux"
|
||||||
|
UNIT_DST="/etc/systemd/system/cyberlux.service"
|
||||||
|
|
||||||
|
# Minimal PATH so systemd finds node/npm when not using login shells
|
||||||
|
cat > "${ENV_FILE}" <<EOF
|
||||||
|
# CyberLux — sourced by cyberlux.service
|
||||||
|
NODE_ENV=production
|
||||||
|
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
|
EOF
|
||||||
|
chmod 0644 "${ENV_FILE}"
|
||||||
|
|
||||||
|
cat > "${UNIT_DST}" <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=CyberLux Next.js (Tor onion backend, 127.0.0.1:3000)
|
||||||
|
Documentation=man:systemd.service(5)
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
# Reverse proxy + hidden services must be up first
|
||||||
|
After=tor.service tor@default.service nginx.service
|
||||||
|
Wants=tor.service nginx.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=${CYBERLUX_USER}
|
||||||
|
Group=${CYBERLUX_GROUP}
|
||||||
|
WorkingDirectory=${CYBERLUX_REPO}
|
||||||
|
EnvironmentFile=-${ENV_FILE}
|
||||||
|
|
||||||
|
# Regenerate onion route maps if onion-nodes.json changed (no network I/O)
|
||||||
|
ExecStartPre=${NODE_BIN} ${CYBERLUX_REPO}/scripts/generate-onion-config.cjs
|
||||||
|
|
||||||
|
ExecStart=${NPM_BIN} run start:onion
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
# Avoid thrashing if Tor/nginx are still starting
|
||||||
|
StartLimitIntervalSec=120
|
||||||
|
StartLimitBurst=5
|
||||||
|
|
||||||
|
NoNewPrivileges=true
|
||||||
|
PrivateTmp=true
|
||||||
|
LimitNOFILE=65535
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chown root:root "${UNIT_DST}"
|
||||||
|
chmod 0644 "${UNIT_DST}"
|
||||||
|
|
||||||
|
if [[ "${CYBERLUX_CHOWN_REPO:-}" == "1" ]]; then
|
||||||
|
chown -R "${CYBERLUX_USER}:${CYBERLUX_GROUP}" "${CYBERLUX_REPO}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable cyberlux.service
|
||||||
|
|
||||||
|
# Best-effort: enable Tor + nginx at boot (unit names vary by distro)
|
||||||
|
systemctl enable tor.service 2>/dev/null || systemctl enable tor@default.service 2>/dev/null || true
|
||||||
|
systemctl enable nginx.service 2>/dev/null || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Installed: ${UNIT_DST}"
|
||||||
|
echo "Env: ${ENV_FILE}"
|
||||||
|
echo "User: ${CYBERLUX_USER}"
|
||||||
|
echo ""
|
||||||
|
echo "Enable boot order (recommended):"
|
||||||
|
echo " systemctl enable tor.service nginx.service cyberlux.service"
|
||||||
|
echo " # or: systemctl enable tor@default.service (distribution-dependent)"
|
||||||
|
echo ""
|
||||||
|
echo "Start now:"
|
||||||
|
echo " systemctl start cyberlux.service"
|
||||||
|
echo " systemctl status cyberlux.service"
|
||||||
|
echo ""
|
||||||
|
echo "Logs:"
|
||||||
|
echo " journalctl -u cyberlux.service -f"
|
||||||
|
echo ""
|
||||||
47
scripts/list-onion-urls.sh
Executable file
47
scripts/list-onion-urls.sh
Executable file
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Print every CyberLux hidden-service URL (reads /var/lib/tor/*/hostname).
|
||||||
|
# Tor creates these files — not Next.js. Use sudo if not readable as your user.
|
||||||
|
#
|
||||||
|
# bash scripts/list-onion-urls.sh
|
||||||
|
# sudo bash scripts/list-onion-urls.sh
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
TOR_DIRS="${REPO}/scripts/generated/tor-dirs.txt"
|
||||||
|
|
||||||
|
if [[ ! -f "${TOR_DIRS}" ]]; then
|
||||||
|
echo "Missing ${TOR_DIRS}. Run: cd ${REPO} && node scripts/generate-onion-config.cjs"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
read_host() {
|
||||||
|
local f="$1"
|
||||||
|
if [[ -r "${f}" ]]; then
|
||||||
|
tr -d '\n' < "${f}"
|
||||||
|
elif command -v sudo >/dev/null 2>&1 && sudo test -r "${f}" 2>/dev/null; then
|
||||||
|
sudo tr -d '\n' < "${f}"
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "CyberLux .onion URLs (http:// only — open in Tor Browser)"
|
||||||
|
echo "────────────────────────────────────────────────────────────"
|
||||||
|
|
||||||
|
while IFS= read -r d || [[ -n "${d}" ]]; do
|
||||||
|
[[ -z "${d}" ]] && continue
|
||||||
|
f="/var/lib/tor/${d}/hostname"
|
||||||
|
host="$(read_host "${f}")"
|
||||||
|
if [[ -n "${host}" ]]; then
|
||||||
|
printf '%-28s http://%s\n' "${d}" "${host}"
|
||||||
|
else
|
||||||
|
printf '%-28s (no hostname yet — %s)\n' "${d}" "${f}"
|
||||||
|
fi
|
||||||
|
done < "${TOR_DIRS}"
|
||||||
|
|
||||||
|
echo "────────────────────────────────────────────────────────────"
|
||||||
|
echo ""
|
||||||
|
echo "If lines show (no hostname yet): wait for Tor, or sudo ls -la /var/lib/tor/"
|
||||||
|
echo ""
|
||||||
109
scripts/onion-status.cjs
Normal file
109
scripts/onion-status.cjs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Lists every Tor hidden-service hostname + nginx loopback URL, and checks
|
||||||
|
* that each loopback port returns HTTP (proves nginx → Next are up for that vhost).
|
||||||
|
*
|
||||||
|
* node scripts/onion-status.cjs
|
||||||
|
* npm run onions:status
|
||||||
|
*
|
||||||
|
* Reading /var/lib/tor/*/hostname usually requires sudo:
|
||||||
|
* sudo node scripts/onion-status.cjs
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const fs = require("fs");
|
||||||
|
const http = require("http");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const REPO = path.join(__dirname, "..");
|
||||||
|
const jsonPath = path.join(__dirname, "onion-nodes.json");
|
||||||
|
const data = JSON.parse(fs.readFileSync(jsonPath, "utf8"));
|
||||||
|
|
||||||
|
function readOnionHost(torDir) {
|
||||||
|
const f = path.join("/var/lib/tor", torDir, "hostname");
|
||||||
|
try {
|
||||||
|
return fs.readFileSync(f, "utf8").trim();
|
||||||
|
} catch (e) {
|
||||||
|
if (e.code === "EACCES" || e.code === "EPERM") {
|
||||||
|
return "(hostname file exists but not readable — try: sudo node scripts/onion-status.cjs)";
|
||||||
|
}
|
||||||
|
if (e.code === "ENOENT") {
|
||||||
|
return "(no hostname yet — is Tor running?)";
|
||||||
|
}
|
||||||
|
return `(${e.message})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkLocalPort(port) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const req = http.request(
|
||||||
|
{
|
||||||
|
hostname: "127.0.0.1",
|
||||||
|
port,
|
||||||
|
path: "/",
|
||||||
|
method: "GET",
|
||||||
|
timeout: 6000,
|
||||||
|
headers: { Connection: "close", Host: "127.0.0.1" },
|
||||||
|
},
|
||||||
|
(res) => {
|
||||||
|
res.resume();
|
||||||
|
resolve(res.statusCode || 0);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
req.on("error", () => resolve(0));
|
||||||
|
req.on("timeout", () => {
|
||||||
|
req.destroy();
|
||||||
|
resolve(0);
|
||||||
|
});
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const rows = [];
|
||||||
|
for (const n of data.nodes) {
|
||||||
|
const onion = readOnionHost(n.torDir);
|
||||||
|
const httpCode = await checkLocalPort(n.port);
|
||||||
|
const ok = httpCode >= 200 && httpCode < 500;
|
||||||
|
rows.push({
|
||||||
|
torDir: n.torDir,
|
||||||
|
port: n.port,
|
||||||
|
kind: n.kind,
|
||||||
|
onion,
|
||||||
|
httpCode,
|
||||||
|
ok,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("");
|
||||||
|
console.log("CyberLux — onion URLs (from /var/lib/tor/*/hostname) + loopback health");
|
||||||
|
console.log("─".repeat(100));
|
||||||
|
let bad = 0;
|
||||||
|
for (const r of rows) {
|
||||||
|
const url =
|
||||||
|
r.onion.startsWith("(") || r.onion.includes("not readable")
|
||||||
|
? r.onion
|
||||||
|
: `http://${r.onion}`;
|
||||||
|
const local = `http://127.0.0.1:${r.port}/`;
|
||||||
|
const status =
|
||||||
|
r.httpCode === 0 ? "DOWN/timeout" : `HTTP ${r.httpCode}`;
|
||||||
|
if (!r.ok) bad++;
|
||||||
|
console.log(`${r.torDir.padEnd(28)} ${String(r.kind).padEnd(12)} ${String(r.port).padEnd(5)} ${status.padEnd(14)} ${url}`);
|
||||||
|
console.log(`${"".padEnd(28)} ${"".padEnd(12)} ${"".padEnd(5)} loopback: ${local}`);
|
||||||
|
console.log("");
|
||||||
|
}
|
||||||
|
console.log("─".repeat(100));
|
||||||
|
if (bad > 0) {
|
||||||
|
console.log(`✗ ${bad} loopback vhost(s) did not return OK HTTP — ensure: systemctl status nginx tor; systemctl status cyberlux`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
console.log("✓ All loopback nginx vhosts responded (Tor .onion forwarding uses these ports).");
|
||||||
|
console.log(" To test via the Tor network use Tor Browser (not curl from clearnet).");
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
process.exit(2);
|
||||||
|
});
|
||||||
@@ -19,8 +19,8 @@ try {
|
|||||||
run("onion config generator", "node scripts/generate-onion-config.cjs");
|
run("onion config generator", "node scripts/generate-onion-config.cjs");
|
||||||
run("TypeScript (tsc --noEmit)", "npx tsc --noEmit");
|
run("TypeScript (tsc --noEmit)", "npx tsc --noEmit");
|
||||||
run(
|
run(
|
||||||
"bash syntax (start.sh, install, backup, restore)",
|
"bash syntax (start.sh, install, backup, restore, systemd, health)",
|
||||||
"bash -n start.sh && bash -n scripts/install-tor-onion.sh && bash -n scripts/backup-onion-keys.sh && bash -n scripts/restore-onion-keys.sh",
|
"bash -n start.sh && bash -n scripts/install-tor-onion.sh && bash -n scripts/install-systemd.sh && bash -n scripts/health-check-stack.sh && bash -n scripts/list-onion-urls.sh && bash -n scripts/backup-onion-keys.sh && bash -n scripts/restore-onion-keys.sh",
|
||||||
);
|
);
|
||||||
run("Next.js production build", "npm run build");
|
run("Next.js production build", "npm run build");
|
||||||
console.log("\n✓ verify: all checks passed.\n");
|
console.log("\n✓ verify: all checks passed.\n");
|
||||||
|
|||||||
6
start.sh
6
start.sh
@@ -12,6 +12,7 @@
|
|||||||
# CYBERLUX_DEPLOY_SEED=n
|
# CYBERLUX_DEPLOY_SEED=n
|
||||||
# CYBERLUX_RANDOM_SEED=1
|
# CYBERLUX_RANDOM_SEED=1
|
||||||
# CYBERLUX_SKIP_ONION_BACKUP=1
|
# CYBERLUX_SKIP_ONION_BACKUP=1
|
||||||
|
# CYBERLUX_PREPARE_ONLY=1 — build + Tor/nginx install, then exit (no Next.js; use with systemd)
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -244,5 +245,10 @@ else
|
|||||||
print_onion_banner
|
print_onion_banner
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "${CYBERLUX_PREPARE_ONLY:-}" == "1" ]]; then
|
||||||
|
echo "[*] CYBERLUX_PREPARE_ONLY=1 — skipping Next.js start (use: systemctl start cyberlux.service)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
echo "[*] Starting Next.js on 127.0.0.1:3000 (Ctrl+C to stop)"
|
echo "[*] Starting Next.js on 127.0.0.1:3000 (Ctrl+C to stop)"
|
||||||
exec npm run start:onion
|
exec npm run start:onion
|
||||||
|
|||||||
Reference in New Issue
Block a user