diff --git a/.gitignore b/.gitignore index 5ef6a52..9a73628 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# runtime-generated onion URL file (populated by scripts/export-onion-urls.sh) +onion-urls.txt diff --git a/ONION-URLS.md b/ONION-URLS.md new file mode 100644 index 0000000..f50bc76 --- /dev/null +++ b/ONION-URLS.md @@ -0,0 +1,107 @@ +# CyberLux — Onion URL Reference + +All 43 Tor v3 hidden services. Live `.onion` addresses are generated at runtime by Tor and stored in `/var/lib/tor//hostname`. This file is a **static registry**; use the commands below to resolve live addresses. + +--- + +## Get Live URLs + +```bash +# Print all live .onion URLs (reads /var/lib/tor/*/hostname) +bash scripts/list-onion-urls.sh +# or with sudo if not readable as your user: +sudo bash scripts/list-onion-urls.sh + +# Save live URLs to onion-urls.txt in the repo root +sudo bash scripts/export-onion-urls.sh +``` + +--- + +## Service Registry + +| # | `torDir` | Port | Role | App Path | +|---|----------|------|------|----------| +| 1 | `cyberlux` | 8080 | Hub — main storefront | `/` | +| 2 | `cyberlux_forum` | 8081 | Void Aggregate — forum | `/forum` | +| 3 | `cyberlux_exchange` | 8082 | Classifieds exchange | `/exchange` | +| 4 | `cyberlux_wiki` | 8083 | Hidden-wiki layer | `/hidden-wiki` | +| 5 | `cyberlux_market` | 8084 | Full catalog / market | `/market` | +| 6 | `cyberlux_barter` | 8085 | Ash Pit barter | `/barter` | +| 7 | `cyberlux_chatter` | 8086 | Chatter / live feed | `/chatter` | +| 8 | `cyberlux_search` | 8087 | Void Crawler search | `/search` | +| 9 | `cyberlux_syndicate` | 8088 | Shell network syndicate | `/syndicate` | +| 10 | `cyberlux_arb_academy` | 8089 | Arb Academy | `/arb-academy` | +| 11 | `cyberlux_reviews` | 8090 | Reviews | `/reviews` | +| 12 | `cyberlux_trust` | 8091 | Trust / reputation layer | `/trust` | +| 13 | `cyberlux_vault` | 8092 | Vault / secure storage | `/vault` | +| 14 | `cyberlux_messages` | 8093 | Private messages | `/messages` | +| 15 | `cyberlux_drop_box` | 8094 | Drop Box | `/drop-box` | +| 16 | `cyberlux_easter_eggs` | 8095 | Easter Eggs | `/easter-eggs` | +| 17 | `cyberlux_links` | 8096 | Curated links | `/links` | +| 18 | `cyberlux_red_room` | 8097 | Red Room | `/red-room` | +| 19 | `cyberlux_drops` | 8098 | Drops | `/drops` | +| 20 | `cyberlux_inner_circle` | 8099 | Inner Circle | `/inner-circle` | +| 21 | `cyberlux_comparison` | 8100 | Comparison tool | `/comparison` | +| 22 | `cyberlux_testimonials` | 8101 | Testimonials | `/testimonials` | +| 23 | `cyberlux_wallets` | 8102 | Wallets | `/wallets` | +| 24 | `cyberlux_support` | 8103 | Support desk | `/support` | +| 25 | `cyberlux_darknet_atlas` | 8104 | Darknet Atlas taxonomy | `/darknet-atlas` | +| 26 | `cyberlux_security_analysis` | 8105 | Security analysis | `/security-analysis` | +| 27 | `cyberlux_mixer` | 8106 | Mixer | `/mixer` | +| 28 | `cyberlux_secret_layer` | 8107 | Secret Layer | `/secret-layer` | +| 29 | `cyberlux_trees` | 8108 | Trees | `/trees` | +| 30 | `cyberlux_presswire` | 8109 | Presswire / news | `/presswire` | +| 31 | `cyberlux_awards` | 8110 | Awards | `/awards` | +| 32 | `cyberlux_raffle` | 8111 | Raffle | `/raffle` | +| 33 | `cyberlux_game` | 8112 | Game | `/game` | +| 34 | `cyberlux_webring` | 8113 | Webring | `/webring` | +| 35 | `cyberlux_conspiracies` | 8114 | Conspiracies | `/conspiracies` | +| 36 | `cyberlux_sanctuary` | 8115 | Sanctuary | `/sanctuary` | +| 37 | `cyberlux_dashboard` | 8116 | Personal relay dashboard | `/dashboard` | +| 38 | `cyberlux_checkout` | 8117 | Checkout flow | `/checkout` | +| 39 | `cyberlux_vendors` | 8118 | Vendors | `/vendors` | +| 40 | `cyberlux_sign_in` | 8119 | Sign in | `/sign-in` | +| 41 | `cyberlux_sign_up` | 8120 | Sign up | `/sign-up` | +| 42 | `cyberlux_account` | 8121 | Account / mirror map | `/account` | +| 43 | `cyberlux_w` | 8122 | Shadow nodes / syndicate shell | `/w` | + +--- + +## Live Address File + +After Tor is running, save all live addresses to a plain-text file: + +```bash +sudo bash scripts/export-onion-urls.sh +# writes: onion-urls.txt +``` + +`onion-urls.txt` (if present in this directory) contains the resolved `http://…56chars….onion` addresses for all services and is gitignored — it reflects runtime state, not source. + +--- + +## How Addresses Stay Stable + +Each service maps to a fixed `HiddenServiceDir` in `/var/lib/tor/`. As long as: + +- the `torDir` name in `scripts/onion-nodes.json` does not change, and +- `/var/lib/tor//` is preserved (or restored from backup), + +the `.onion` address **never rotates**, surviving reboots, rebuilds, and Tor restarts. + +Backup/restore: + +```bash +sudo bash scripts/backup-onion-keys.sh # saves to /var/backups/cyberlux-onion-keys/current +sudo bash scripts/restore-onion-keys.sh # restores missing dirs from backup +``` + +--- + +## Notes + +- All services run from **one** Next.js instance at `127.0.0.1:3000`. +- nginx vhosts on `127.0.0.1:8080–8122` proxy to Next with `X-Cyberlux-Node` headers for host-aware routing. +- Open addresses in **Tor Browser** or **Onion Browser** (iOS). Standard browsers cannot resolve `.onion`. +- Use `http://` — TLS inside the onion circuit is handled end-to-end by Tor. diff --git a/README.md b/README.md index bc33583..fa059df 100644 --- a/README.md +++ b/README.md @@ -2,199 +2,155 @@ > **The clearnet is a showroom. The onion is the door. One engine behind every door.** -We did not build forty-three repos. We built **one** Next.js stack and **refused** to fracture it—then we **projected** it through Tor until every vertical had its own v3 address, its own myth, its own entry point. Nginx sits in the middle like a bouncer; Tor does the rest. Same codebase. Same deploy. **Different `.onion` front doors** with host-aware rewrites (`proxy.ts` + `X-Cyberlux-Node`). - -This is not a route demo. It is **infrastructure with intent**: stable `HiddenServiceDir` names, backup and restore for onion keys, Tor + nginx generated from **one JSON source of truth**. It is built to **boot, survive operator mistakes, and stay legible when everything else is on fire**. +One Next.js stack projected through 43 independent Tor v3 hidden services. Nginx sits in the middle; Tor does the publishing. Same codebase, same deploy — **different `.onion` front doors** with host-aware rewrites (`proxy.ts` + `X-Cyberlux-Node`). No fork army. No split deployments. --- -## The thesis +## Architecture -- **One truth, many masks** — one build, dozens of onions; no fork army. -- **Identity persists** — keys and dirs are first-class; restarts don’t erase your address book. -- **Loopback only** — Next and nginx don’t audition on the public internet; Tor publishes what you mean to publish. -- **Verify or rot** — `npm run verify` is the line between shipping and LARP. +``` + Tor Browser + │ http://<56-char>.onion + ▼ + [ Tor Network ] + │ + ▼ + nginx 127.0.0.1:8080–8122 (one vhost per .onion, host header → X-Cyberlux-Node) + │ + ▼ + Next.js 127.0.0.1:3000 (single instance, proxy.ts routes by header) +``` + +**43 onion services → one app → zero public ports.** --- -## Operator controls - -| 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 | -| **`DEPLOY.md`** | Firewall posture, compliance reminder, full systemd notes | - -**Tor on a phone:** **Onion Browser** (iOS) or **Tor Browser for Android** — Safari and Chrome will **never** resolve `.onion`. Paste the full `http://` + 56-char host + `.onion`. Some carriers fight Tor; Wi‑Fi often wins. - ---- - -## What ships in the box - -- `43` Tor v3 services -- stable loopback range `127.0.0.1:8080–8122` -- Next bound only to `127.0.0.1:3000` -- one `.onion` per major surface -- persistent hidden-service identity keyed by `HiddenServiceDir` -- self-healing backup / restore of onion key directories - -## What it does (the map) - -CyberLux boots **one** Next.js app and **projects** it through multiple onion entry points. - -- `hub` — main storefront -- `forum` — Void Aggregate -- `exchange` — classifieds stack -- `market` — full catalog vertical -- `search` — Void Crawler -- `wiki` — hidden-wiki layer -- `w` — syndicate shell network -- dozens more dedicated onions map to top-level app routes - -The routing layer uses host-aware rewrites so a dedicated onion **feels** like its own property without splitting the app into forty-three deployments. - -## One-command launch - -Install system deps on Debian/Ubuntu: +## Quick Start ```bash sudo apt install tor nginx -``` - -Then: - -```bash chmod +x start.sh ./start.sh ``` -What `./start.sh` does: +Open the hub in **Tor Browser** (addresses print at the end of start.sh). +Local-only (skip Tor/nginx): `CYBERLUX_SKIP_TOR=1 ./start.sh` -1. regenerates Tor / nginx / app route maps from `scripts/onion-nodes.json` -2. runs `git pull --ff-only` if the repo has `.git` (failure is non-fatal) -3. runs `npm install` -4. repairs `.next` ownership if a root-owned build left it unwritable -5. runs a production build -6. restores backed-up onion keys if any hidden-service directories are missing -7. installs Tor + nginx config only when it actually changed -8. waits for all onion hostname files, not just the hub -9. refreshes the onion key backup set -10. prints every live `.onion` URL -11. starts Next on `127.0.0.1:3000` +--- -Open in Tor Browser: +## Operator Commands -```text -http://<56-char-v3-address>.onion +| Command | What it does | +|---------|-------------| +| `./start.sh` | Full pipeline: generate → build → Tor/nginx → print URLs → start Next | +| `npm run onions:list` | Print all live `.onion` URLs from `/var/lib/tor/*/hostname` | +| `sudo npm run onions:export` | Write live URLs to `onion-urls.txt` in repo root | +| `npm run onions:status` | URLs + HTTP probe each nginx vhost | +| `npm run health:stack` | Curl Tor/nginx/Next loopbacks and report status | +| `npm run verify` | Full pre-ship check: config gen, TS, shell syntax, build | +| `sudo bash scripts/install-tor-onion.sh` | Install/refresh Tor + nginx config | +| `sudo bash scripts/backup-onion-keys.sh` | Back up all hidden-service key dirs | +| `sudo bash scripts/restore-onion-keys.sh` | Restore missing key dirs from backup | +| `sudo bash scripts/install-systemd.sh` | Install `cyberlux.service` for boot-time start | +| `sudo bash scripts/classroom-ufw.sh` | Lock firewall to loopback-only (recommended) | + +--- + +## Onion URL Reference + +See [`ONION-URLS.md`](ONION-URLS.md) — full table of all 43 services with ports and app paths. + +To export live `.onion` addresses after Tor is running: + +```bash +sudo bash scripts/export-onion-urls.sh +# writes onion-urls.txt (gitignored — reflects runtime state) ``` -## Persistent onion addresses +--- -Yes — the onion addresses are **meant** to persist. +## What Ships in the Box -Each service uses a fixed Tor directory: +- **43** Tor v3 hidden services +- Loopback range `127.0.0.1:8080–8122` (nginx vhosts, never public) +- Next.js bound only to `127.0.0.1:3000` +- One `.onion` per major surface — hub, forum, market, exchange, wiki, and 38 more +- Persistent hidden-service identity keyed by `HiddenServiceDir` name +- Self-healing backup / restore for onion keys +- BTCPay Server integration (configurable) for on-chain + Lightning deposits +- Systemd unit generator for unattended boot -```tor -HiddenServiceDir /var/lib/tor/ -HiddenServicePort 80 127.0.0.1: +--- + +## What `./start.sh` Does (in order) + +1. Regenerates Tor / nginx / app route maps from `scripts/onion-nodes.json` +2. Runs `git pull --ff-only` if repo has `.git` (non-fatal on failure) +3. Runs `npm install` +4. Repairs `.next` ownership if a root-owned build left it unwritable +5. Runs a production build +6. Restores backed-up onion keys if any hidden-service directories are missing +7. Installs Tor + nginx config (only when it changed) +8. Waits for **all** onion hostname files, not just the hub +9. Refreshes the onion key backup set +10. Prints every live `.onion` URL +11. Starts Next on `127.0.0.1:3000` + +--- + +## Source of Truth + +Everything — Tor config, nginx vhosts, app route maps — is generated from one file: + +``` +scripts/onion-nodes.json ``` -The `.onion` stays stable as long as: +Generated artifacts (do not edit manually): + +| File | Description | +|------|-------------| +| `tor/cyberlux-nodes.conf` | Tor `HiddenServiceDir` / `HiddenServicePort` blocks | +| `nginx/cyberlux-onion-servers.inc` | nginx server blocks (one per onion) | +| `lib/onionRoutes.generated.ts` | TypeScript route map used by `proxy.ts` | +| `scripts/generated/tor-dirs.txt` | Newline-separated list of service dir names | +| `scripts/generated/onion-labels.tsv` | Human-readable labels per service | +| `scripts/generated/onion-port-range.txt` | Port min / max for scripts | + +--- + +## Persistent Onion Addresses + +Addresses are stable as long as: - `torDir` names in `scripts/onion-nodes.json` do not change -- `/var/lib/tor/` is preserved +- `/var/lib/tor//` is preserved - or the backup set can restore those directories -Normal app restarts, rebuilds, and standard Tor restarts do **not** rotate addresses by design. +Normal app restarts, rebuilds, and standard Tor restarts **do not rotate addresses**. -## Self-healing onion identity +Backup location: `/var/backups/cyberlux-onion-keys/current` -- `scripts/backup-onion-keys.sh` -- `scripts/restore-onion-keys.sh` +--- -Backup location: +## BTCPay Server -```text -/var/backups/cyberlux-onion-keys/current -``` +The checkout flow supports BTCPay Server for on-chain + Lightning payments. Configure with environment variables (see `.env.example` or `DEPLOY.md`). The stack runs without BTCPay — the API returns `503` if not configured. -After boot, ready onion service directories are backed up. If a service dir is missing from `/var/lib/tor` next boot, it is restored from backup. That is the gap between **screenshotware** and **survives a tired operator**. +--- -## Tor / nginx topology - -Source of truth: `scripts/onion-nodes.json` - -Generated artifacts: - -- `tor/cyberlux-nodes.conf` -- `nginx/cyberlux-onion-servers.inc` -- `lib/onionRoutes.generated.ts` -- `scripts/generated/tor-dirs.txt` -- `scripts/generated/onion-labels.tsv` -- `scripts/generated/onion-port-range.txt` - -Install path: - -- `/etc/tor/cyberlux-nodes.conf` -- `/etc/nginx/cyberlux-server-common.inc` -- `/etc/nginx/cyberlux-onion-servers.inc` -- `/etc/nginx/sites-available/cyberlux-onion` - -Runtime: - -- Next.js: `127.0.0.1:3000` -- nginx onion vhosts: `127.0.0.1:8080–8122` -- Tor exposes the public `.onion` endpoints - -## Manual ops - -Install or refresh Tor/nginx config: - -```bash -sudo bash scripts/install-tor-onion.sh -``` - -Back up onion keys: - -```bash -sudo bash scripts/backup-onion-keys.sh -``` - -Restore missing onion keys: - -```bash -sudo bash scripts/restore-onion-keys.sh -``` - -Local-only, skip Tor/nginx: - -```bash -CYBERLUX_SKIP_TOR=1 ./start.sh -``` - -Start app without the helper: - -```bash -npm run build -npm run start:onion -``` - -## Boot at startup (systemd) - -Generate the unit with your Unix user and repo path: +## Boot at Startup (systemd) ```bash sudo CYBERLUX_USER=$USER bash scripts/install-systemd.sh sudo systemctl enable --now tor.service nginx.service cyberlux.service -# use `tor@default.service` instead of `tor.service` if your distro names it that way ``` -The installer writes `/etc/systemd/system/cyberlux.service` and `/etc/default/cyberlux`. Legacy template notes live in `scripts/cyberlux.service` (prefer the generator). +Use `tor@default.service` instead of `tor.service` if your distro names it that way. -Tor + nginx must already be configured (`./start.sh` or `sudo bash scripts/install-tor-onion.sh` at least once). +Tor + nginx must already be configured (`./start.sh` or `sudo bash scripts/install-tor-onion.sh` first). + +--- ## Verification @@ -202,67 +158,76 @@ Tor + nginx must already be configured (`./start.sh` or `sudo bash scripts/insta npm run verify ``` -Checks: onion config generation, TypeScript, shell syntax for boot/install/backup/restore scripts, full Next production build. +Checks: config generation, TypeScript compilation, shell syntax for all boot/install/backup/restore scripts, full Next.js production build. -## Common failure: `.next` permission hell +--- -If you built with `sudo`, `.next` may be root-owned and Next fails with `EACCES`. +## Common Fixes + +**`.next` permission error (built with sudo):** ```bash sudo bash scripts/fix-next-perms.sh ./start.sh ``` -## Security (read twice) +**Onion hostnames not showing up:** -CyberLux is hardened for a **demo stack**, not sold as magic invisibility. +```bash +sudo systemctl status tor +sudo ls -la /var/lib/tor/ +# Wait ~30s after Tor starts for v3 descriptor publication +``` -- Next and nginx bind to loopback only -- Tor exposes the onion endpoints -- do **not** publish `3000` or `8080–8122` to the public internet -- optional host firewall: `sudo bash scripts/classroom-ufw.sh` -- bad application logic stays bad behind Tor -- operational mistakes beat branding every time +**Port 3000 already in use:** -Tor obscures **reachability** better than raw-IP hosting. It does not forgive bad code, bad habits, or bad judgment. - -## Site map - -Core verticals: - -- `/` hub storefront -- `/market` full catalog -- `/forum` Void Aggregate -- `/exchange` classifieds -- `/barter` Ash Pit -- `/search` Void Crawler -- `/hidden-wiki` wiki layer -- `/darknet-atlas` taxonomy / atlas -- `/syndicate` shell network -- `/dashboard` personal relay -- `/account/hidden-services` mirror map - -Additional dedicated onions exist for many top-level routes beyond these. - -## Stack - -- Next.js `16` -- React `19` -- Tailwind CSS `4` -- nginx -- Tor hidden services -- generated route / host mapping +```bash +ss -ltnp | grep 3000 +# Stop the conflicting process, then ./start.sh +``` --- -## Final word +## Security -If you are running this, you are **past cosplay**. The stack exists to: +- Next.js and nginx bind to **loopback only** — they are never public +- Tor publishes the `.onion` endpoints +- Do **not** expose ports 3000 or 8080–8122 to the internet +- Harden with: `sudo bash scripts/classroom-ufw.sh` +- Tor obscures reachability — it does not forgive bad code or bad ops -- keep **onion identities** across rebuilds when you respect `torDir` names and backups -- expose **only** what Tor publishes — not your loopback to the raw internet -- fail **loud** in verification instead of silently rotting +Read `DEPLOY.md` before pointing real users at this stack. -Read **`DEPLOY.md`** before you point real people at it. **You** own jurisdiction, opsec, and what you ship. The network does not owe you anonymity; **you** owe the network discipline. +--- -**This README is the product’s declaration of intent. The code is the rest.** +## Site Map + +| Vertical | Path | Onion | +|---------|------|-------| +| Hub storefront | `/` | `cyberlux` | +| Forum (Void Aggregate) | `/forum` | `cyberlux_forum` | +| Exchange (classifieds) | `/exchange` | `cyberlux_exchange` | +| Market (full catalog) | `/market` | `cyberlux_market` | +| Barter (Ash Pit) | `/barter` | `cyberlux_barter` | +| Search (Void Crawler) | `/search` | `cyberlux_search` | +| Hidden Wiki | `/hidden-wiki` | `cyberlux_wiki` | +| Syndicate (shell net) | `/syndicate` | `cyberlux_syndicate` | +| Dashboard | `/dashboard` | `cyberlux_dashboard` | +| Account / mirror map | `/account` | `cyberlux_account` | +| + 33 more | see `ONION-URLS.md` | | + +--- + +## Stack + +- **Next.js 16** · React 19 · Tailwind CSS 4 +- **nginx** reverse proxy (loopback vhosts) +- **Tor** v3 hidden services +- **BTCPay Server** (optional, on-chain + Lightning) +- Generated route / host mapping from single JSON source + +--- + +> If you are running this, you are past cosplay. +> The network does not owe you anonymity — **you** owe the network discipline. +> Read `DEPLOY.md`. Own your jurisdiction. diff --git a/app/api/btcpay/status/[invoiceId]/route.ts b/app/api/btcpay/status/[invoiceId]/route.ts index 618d6e9..c68f16e 100644 --- a/app/api/btcpay/status/[invoiceId]/route.ts +++ b/app/api/btcpay/status/[invoiceId]/route.ts @@ -3,13 +3,13 @@ import { getBtcPayInvoiceStatus, isBtcPayConfigured } from "@/lib/btcpay"; export async function GET( _req: Request, - { params }: { params: { invoiceId: string } }, + { params }: { params: Promise<{ invoiceId: string }> }, ) { if (!isBtcPayConfigured()) { return NextResponse.json({ ok: false, error: "BTCPay not configured" }, { status: 503 }); } - const { invoiceId } = params; + const { invoiceId } = await params; if (!invoiceId || invoiceId.length < 4) { return NextResponse.json({ ok: false, error: "Invalid invoice ID" }, { status: 400 }); } diff --git a/package.json b/package.json index 35f99ef..69e7c3a 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "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'" + "install:systemd": "bash -c 'echo Run: sudo CYBERLUX_USER=$USER bash scripts/install-systemd.sh'", + "onions:export": "bash scripts/export-onion-urls.sh" }, "dependencies": { "next": "16.1.7", diff --git a/scripts/export-onion-urls.sh b/scripts/export-onion-urls.sh new file mode 100755 index 0000000..42c01f8 --- /dev/null +++ b/scripts/export-onion-urls.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# Export all live CyberLux .onion URLs to onion-urls.txt in the repo root. +# Reads /var/lib/tor/*/hostname — run with sudo if needed. +# +# bash scripts/export-onion-urls.sh +# sudo bash scripts/export-onion-urls.sh +# +set -euo pipefail + +REPO="$(cd "$(dirname "$0")/.." && pwd)" +TOR_DIRS="${REPO}/scripts/generated/tor-dirs.txt" +LABELS="${REPO}/scripts/generated/onion-labels.tsv" +OUT="${REPO}/onion-urls.txt" + +if [[ ! -f "${TOR_DIRS}" ]]; then + echo "[!] Missing ${TOR_DIRS}. Run: node scripts/generate-onion-config.cjs" + exit 1 +fi + +declare -A ROLE=() +if [[ -f "${LABELS}" ]]; then + while IFS=$'\t' read -r dir desc || [[ -n "${dir}" ]]; do + [[ -z "${dir}" ]] && continue + ROLE["${dir}"]="${desc}" + done < "${LABELS}" +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 "# CyberLux — Live .onion URLs" + echo "# Generated: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" + echo "# Open in: Tor Browser / Onion Browser (iOS) — http:// only" + echo "#" + echo "" + + ready=0 + missing=0 + while IFS= read -r d || [[ -n "${d}" ]]; do + [[ -z "${d}" ]] && continue + f="/var/lib/tor/${d}/hostname" + host="$(read_host "${f}")" + role="${ROLE[$d]:-}" + if [[ -n "${host}" ]]; then + printf '%-28s http://%s\n' "${d}" "${host}" + [[ -n "${role}" ]] && printf '%-28s %s\n' "" "# ${role}" + echo "" + ((ready++)) || true + else + printf '%-28s (not available — %s)\n' "${d}" "${f}" + echo "" + ((missing++)) || true + fi + done < "${TOR_DIRS}" + + echo "# Services resolved: ${ready}" + [[ "${missing}" -gt 0 ]] && echo "# Services pending: ${missing} (start Tor and wait ~30s)" +} > "${OUT}" + +echo "[*] Wrote ${OUT}" +echo "[*] Services resolved: ${ready} / $((ready + missing))" +[[ "${missing}" -gt 0 ]] && echo "[!] ${missing} hostname(s) not yet available — run again after Tor fully starts."