Adds the 5 chapters that hit a transient rate-limit on first pass: - 01 Architecture & the Shared DNA - 02 Building a New Volume (the Cookbook) - 10 Vol I Internals — qBitTorrz - 12 Vol III Internals — MACRO_VIRUS.XLS - 13 Vol IV Internals — UPLINK The Dev Handbook (Disc One) is now complete at 10 chapters; the Lore Bible (Disc Two) was already complete. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
19 KiB
Vol I Internals — qBitTorrz
The annotated internals of the baseline volume: state shape, the two-term download model, seeding income, the catalog tier ladder, the swarm systems, hardware tiers, the upgrade store, and Client Migration. Everything here is cited from index.html; the numbers are the real numbers.
qBitTorrz is the reference implementation. Every later volume re-derives the scaffolding it establishes — freshState/save/load/sanitizeState, the dt-capped tick, simulateAway offline catch-up, toast. The novel hook is the two-term download model: a torrent downloads at min(Max Line Speed, Seeds × Bandwidth/Peer), and almost every system exists to push one of those two terms.
The G state shape
G is the single global, seeded by freshState() (line 530) and round-tripped through localStorage under SAVE_KEY = "qbittorrz_save_v1". The annotated keys:
| Key | Type | Meaning |
|---|---|---|
v |
number | Save schema version (1). |
data |
number | Data — the spendable currency (bytes uploaded, banked). |
totalUp / totalDown |
number | Lifetime-of-run upload / download counters (HUD + ratio). |
runUp |
number | Upload accrued this prestige run — the sole input to creditsFor. |
credits |
number | Crypto-Credits (prestige currency). |
prestiges |
number | Migration count. |
lifetimeUp |
number | Upload across all runs; never reset. |
torrents |
array | Active torrents (each {uid,cid,name,downloaded,uploaded,status,…}). |
upgrades |
object | id → level; cleared on prestige. |
perks |
object | id → level; persists across prestige. |
seq |
number | Monotonic torrent-uid source. |
sel |
number/null | Selected torrent uid. |
filter |
string | Sidebar status filter (all/downloading/…). |
dtab |
string | Active detail tab. |
sci |
bool | Scientific-notation toggle (View menu). |
seenCatalog |
object | Catalog ids already viewed in the Add dialog (drives the new badge). |
trackerList |
array | Announced trackers {url,seeds,peers,status}. |
rssFilter / rssOn |
string / bool | RSS glob and auto-add toggle. |
burstUntil,nextBurst,throttleUntil,nextThrottle |
number | Event timers (ms). |
irc,nextIrc,nextMagnet |
array/number | IRC log and drop schedulers. |
lastSave,started |
number | Timestamps; lastSave anchors offline catch-up. |
sidebarW,detailH |
number | Persisted splitter geometry. |
sanitizeState() (line 1577) repairs every one of these — num() clamps non-finite/negative numbers, object fields that arrive as arrays or null are reset, and torrents whose cid is not in CAT_BY_ID are dropped. A future-dated lastSave self-heals so a clock skew can't grant infinite offline time.
The download model
This is the load-bearing system. Read it in three parts: the line (your ceiling), the peer fraction (how much of the ceiling you actually get), and the realization that multiplies them.
The line: clientDown() and sqrt scaling
lineSpeed() is literally clientDown() (line 625). Line speed is not a fixed hardware number — it scales with the largest file you've unlocked:
const BASE_DOWN = 1000; // B/s anchor before scaling/multipliers
const MIN_SIZE = 4e3; // smallest catalog file (Tier A)
function maxUnlockedSize(){
let m = MIN_SIZE;
for(const c of CATALOG){ if(c.size>m && catalogUnlocked(c)) m=c.size; }
return m;
}
function clientDown(){ return BASE_DOWN * Math.sqrt(maxUnlockedSize()/MIN_SIZE) * downMult(); }
The sqrt is the keystone. File sizes span seventeen orders of magnitude (4e3 → 3.0e20); a linear line term would make late files either trivial or eternal. The square root halves the exponent the player must cover with bandwidth upgrades while leaving big files a real sink. Worked, with downMult()===1:
| Largest unlocked | clientDown() |
|---|---|
Tier A only (4e3) |
1,000 B/s |
Cat6 (Wikipedia 9.0e8) |
≈ 474,342 B/s |
Private (Every_Linux_ISO_Ever 2.4e11) |
≈ 7,745,967 B/s |
Fiber (CERN…root 1.9e14) |
≈ 217,944,947 B/s |
Singularity (The_Final_Hash 3.0e20) |
≈ 2.74e11 B/s |
DEV NOTE — unlocking a tier is the bandwidth grant. Because
maxUnlockedSize()reads the catalog throughcatalogUnlocked, buyingcat6simultaneously reveals MB-class files and raises the line to download them, so the player never unlocks content they're structurally unable to finish. The corollary:maxUnlockedSize()iterates the fullCATALOGon everyclientDown()call, andclientDown()runs per-torrent per-tick — cheap at 23 entries, but the first thing to memoize if the catalog grows.
downMult() (line 568) stacks the multiplicative download bonuses: bandwidth (×2.2/level via HW_TIERS), overclock (×1.8/level), the dpi perk, the ×10 DHT burst while now()<G.burstUntil, and the ×0.4 ISP throttle while now()<G.throttleUntil && !hasUnlock('quantum').
The peer fraction: seeds and downFrac
Seeds are assembled bottom-up. torrentSeeds(t) (line 616) sums the catalog swarm, baseSeedBonus(), and trackerSeedBonus(), then applies a per-torrent prioritization multiplier:
function torrentSeeds(t){
const c=CAT_BY_ID[t.cid];
const base=(c?c.seeds:1)+baseSeedBonus()+trackerSeedBonus();
return Math.max(1, Math.round(base*(1+peerPrioBonus(t))));
}
baseSeedBonus()=2*lvl('upnp')+ 2 ifmagnetis owned +2*plvl('seed0')— global seed floor.trackerSeedBonus()sumsseedsoverG.trackerList— the announce loop.peerPrioBonus(t)=+0.15fordropLeechersand+0.15forprioSeeds(the two Peers-tab checkboxes), applied multiplicatively to the assembled base.
The fraction of the line you actually hit:
const SAT_SEEDS = 12; // connected seeds that saturate the line
function perPeerMult(){ return Math.pow(UP_BY_ID.perpeer.mult, lvl('perpeer')); } // ×1.3/level
function downFrac(seeds){ return Math.min(1, seeds/SAT_SEEDS*perPeerMult()); }
Below SAT_SEEDS worth of effective peers you are peer-limited: a fresh catalog file with seeds: 6 and no perpeer upgrades runs at 6/12 = 0.50 — half its line. The two ways out are announcing trackers (more seeds) and perpeer (each level multiplies the fraction by 1.3, so the General tab reports the falling "seeds to saturate" count). At 12 effective seeds, downFrac clamps to 1.0 and the torrent goes line-limited.
The realization
function torrentDownSpeed(t){
if(t.status!=='downloading') return 0;
return lineSpeed() * downFrac(torrentSeeds(t));
}
That product is the in-game promise made concrete: lineSpeed × min(1, seeds/SAT_SEEDS × perPeerMult) equals min(lineSpeed, lineSpeed × seeds × perPeerMult / SAT_SEEDS) — the line, or the peer term, whichever binds. The General tab labels which is active via the (line-limited) / (peer-limited) tag from downFrac(...)>=1.
Seeding income
Income is simpler — there is no peer gating on upload. A completed torrent flips to seeding and earns:
function torrentUpSpeed(t){
if(t.status!=='seeding') return 0;
const c=CAT_BY_ID[t.cid];
return (c.up * upMultGlobal()) + seedboxFlat();
}
c.up is the per-torrent base income. upMultGlobal() (line 577) multiplies upslot (×1.55/level), compress (×1.8/level), ratio (×1.12/level), the ghost perk, and the same ×0.4 throttle penalty. seedboxFlat() is lvl('seedbox') * 5e5 — a flat addend per torrent, strongest on small files and dominant early after Private. In step() (line 668) the gain lands in four counters at once: G.data, G.totalUp, G.runUp, G.lifetimeUp. The status-bar Ratio is the run aggregate G.totalUp/G.totalDown, distinct from each torrent's own uploaded/c.size.
The catalog and tier gating
CATALOG (line 402) is 23 fixed entries — {id,name,size,up,seeds,req,rare?}. req is the unlock id that reveals the entry; catalogUnlocked(c) resolves it, routing darknet/singularity to hasPerkUnlock (prestige perks) and everything else to hasUnlock (bought upgrades):
function catalogUnlocked(c){
if(!c.req) return true;
if(c.req==='darknet') return hasPerkUnlock('darknet');
if(c.req==='singularity') return hasPerkUnlock('singularity');
return hasUnlock(c.req);
}
The six tiers, each ~10³–10⁴× the previous in both size and income:
| Tier | req |
Size range | Example | Gated by |
|---|---|---|---|---|
| Public A | null |
4 kB – 6.5 MB | tiny_release_notes.txt |
always |
| Public B | cat6 |
44 MB – 900 MB | Wikipedia_Full_Text_Dump.xml |
Cat6 upgrade |
| Private | private |
4.2 GB – 240 GB | GameOfThorns.S01.REMUX.2160p |
Private Tracker Invite |
| Fiber | fiber |
1.6 TB – 190 TB | CERN_Collider_Event_Logs.root |
Fiber-Optic Trunk |
| Darknet | darknet |
1.3 PB – 600 PB | The_Library_Of_Everything.iso |
perk (prestige) |
| Singularity | singularity |
5 EB – 300 EB | The_Final_Hash.dat |
perk (gated behind Darknet) |
rare:true entries (t11, t15, t19, t22) stall at 99.9% — see the swarm systems. The unlock chain Public → Cat6 → Private → Fiber → Darknet → Singularity is enforced by the upgrades' reqUp (private requires cat6; fiber requires private) and the perks' reqPerk (singularity requires darknet). The last two tiers are only reachable through migration, which is the structural reason to prestige.
The swarm systems
Trackers / announce
announceTracker() (line 736) spends Data to push a {url,seeds,peers,status} onto G.trackerList, drawing from TRACKER_URLS (falling back to generated .onion relays once the pool is exhausted). Cost escalates with how many you've added past the two defaults:
function announceCost(){
const added=Math.max(0, (G.trackerList||[]).length-2);
return Math.max(120, totalUpSpeed()*18) * Math.pow(1.85, added);
}
Each tracker adds 2–4 seeds to every torrent (via trackerSeedBonus), which is the primary lever against peer-limiting.
Peer prioritization
The Peers detail tab exposes two per-torrent checkboxes, dropLeechers and prioSeeds, wired through peer-toggle. Each contributes +0.15 in peerPrioBonus(t), so both ticked is ×1.30 on assembled seeds. (The per-row Prioritize/Ban buttons are cosmetic — peer-boost nudges burstUntil by 2.5 s, peer-ban only toasts.)
RSS glob filter
globToRe(glob) (line 715) compiles a shell-glob into a case-insensitive RegExp: it escapes regex metacharacters, then maps * → .* and ? → ., defaulting to /.*/i on empty or malformed input. When rss is unlocked, rssAutoAdd() runs each tick: if a slot is free, it filters the catalog to unlocked entries matching rssFilter, sorts by up descending, and adds the single highest-income match. It is a hands-off "always fill the best slot" automation, not a queue.
IRC announce-bot magnets
ircMagnetDrop() (line 751) periodically pushes a magnet line tagging a random unlocked cid; the IRC tab renders it with a clickable [🧲 grab]. grabMagnet(cid) calls addTorrent(cid, true, 0.25) — the third arg is a head-start fraction, so the torrent is born 25% downloaded, skipping the slow peer-limited opening:
function addTorrent(cid, silent, headstart){
// …
downloaded: headstart? Math.min(c.size*headstart, c.size*0.999) : 0,
The Math.min(..., c.size*0.999) cap matters for rares — a head-start can't accidentally vault a rare torrent past its stall point.
DEV NOTE — the rare-stall mechanic is the one place the idle contract bites. A
raretorrent freezes at exactlyc.size*0.999, setsstatus:'stalled', and earns nothing untilforceComplete()(the Request last piece /bribeaction) seeds the final block. BecausesimulateAway()reproduces the same stall logic (line 1636), a rare left downloading while you're away sits at 99.9% — not seeding — when you return. Intended friction, but the canonical "why didn't my offline earnings include that torrent" support question. See QA Playbook & Post-Mortems for the broader dead-zone discussion.
DHT bursts and ISP throttle
handleEvents() (line 679) schedules both. With dht owned, a burst sets burstUntil = now()+8000 and a ×10 download multiplier fires in downMult(), on a 45–90 s cadence. With private owned but not quantum, the ISP throttle sets throttleUntil for 9–16 s and applies ×0.4 to both downMult() and upMultGlobal(). quantum (Quantum Encryption Protocol) is a hard immunity — the throttle branch checks !hasUnlock('quantum') in three places (the two multipliers and the status banner).
Hardware tiers
HW_TIERS (line 494) is the 13-name ladder the bandwidth upgrade walks. hwTierName() clamps the level into the array and appends a +N suffix past the end:
const HW_TIERS = ['56k Dial-up Modem','ISDN Line','ADSL Broadband','SDSL Line','Cable Broadband',
'VDSL2','Gigabit Fiber','10G Fiber','Metro Ethernet','OC-768 Backbone','Tier-1 Telecom Trunk',
'Dark Fiber Mesh','Quantum Backbone'];
function hwTierName(){
const l=lvl('bandwidth');
return HW_TIERS[Math.min(l,HW_TIERS.length-1)] + (l>=HW_TIERS.length?(' +'+(l-HW_TIERS.length+1)):'');
}
These names are pure flavor over the bandwidth multiplier — throughput comes from downMult(), not the string — but they anchor the lore's climb from dial-up to a Quantum Backbone.
The upgrades store
UPGRADES (line 438) drives the three-tab store (public / private / darknet). Repeatable upgrades cost floor(base * growth^level); unlock kinds are one-time at base. The full table:
| id | tab | kind | base | growth | effect | cap |
|---|---|---|---|---|---|---|
bandwidth |
public | hardware | 64 | 1.7 | ×2.2 line / level | 60 |
perpeer |
public | perpeer | 320 | 1.6 | ×1.3 per-peer / level | 30 |
upslot |
public | mult-up | 90 | 1.55 | ×1.55 income / level | 200 |
upnp |
public | seeds | 140 | 1.7 | +2 seeds / level | 60 |
storage1 |
public | storage | 600 | 1.9 | +1 slot / level | 40 |
ratio |
public | ratio | 260 | 1.5 | ×1.12 income / level | 150 |
cat6 |
public | unlock | 1,800 | — | reveal Public B | — |
dht |
public | unlock | 4,200 | — | enable ×10 bursts | — |
private |
private | unlock | 9,000 | — | reveal Private (req cat6) |
— |
magnet |
private | unlock | 24,000 | — | +2 base seeds (req private) |
— |
rss |
private | unlock | 60,000 | — | RSS auto-add (req private) |
— |
seedbox |
private | flat-up | 40,000 | 1.8 | +5e5 flat income / level | 120 |
storage2 |
private | storage | 5e4 | 1.75 | +1 slot / level (req private) |
60 |
fiber |
darknet | unlock | 5e6 | — | reveal Fiber (req private) |
— |
quantum |
darknet | unlock | 2e7 | — | throttle immunity (req fiber) |
— |
overclock |
darknet | mult-down | 3e7 | 1.7 | ×1.8 download / level (req fiber) |
200 |
compress |
darknet | mult-up | 3.5e7 | 1.7 | ×1.8 income / level (req fiber) |
200 |
upgradeReqMet(u) enforces reqUp; buyUpgrade(id) (line 838) deducts Data, increments the level, and on unlocks fires diegetic IRC system lines (cat6, private, fiber). maxSlots() is 3 + storage1 + storage2 + plvl('rep') — the storage upgrades plus the prestige rep perk.
Prestige: Client Migration
Prestige is reframed as redeploying onto a Virtual Seedbox Cluster. The yield is sub-linear in run-upload:
function creditsFor(runUp){
if(runUp<1e9) return 0;
return Math.floor(Math.pow(runUp/1e9, 0.42));
}
So 1e9 → 1 credit, 1e10 → 2, 1e12 → 18. The 1e9 floor is the gate the toolbar button and modal enforce. doPrestige() (line 884) banks the gain and performs a surgical reset:
G.credits+=gain; G.prestiges++;
G.data=0; G.runUp=0; G.torrents=[]; G.upgrades={}; G.sel=null;
G.trackerList=defaultTrackers();
G.burstUntil=0;G.nextBurst=0;G.throttleUntil=0;G.nextThrottle=0;G.nextMagnet=0;
Wiped: Data, run upload, torrents, all bought upgrades, trackers, and event timers. Kept: credits, perks, prestiges, lifetimeUp. Credits buy PERKS (line 480) via buyPerk:
| id | persistent effect | base cost | growth | cap |
|---|---|---|---|---|
dpi |
+25% global download / level | 1 | 1.6 | 50 |
ghost |
+25% global income / level | 1 | 1.6 | 50 |
rep |
+1 permanent slot / level | 2 | 1.8 | 25 |
seed0 |
+2 base seeds / level | 2 | 1.7 | 25 |
darknet |
unlock Petabyte tier (once) | 3 | — | — |
singularity |
unlock Exabyte+ tier (once, req darknet) |
40 | — | — |
darknet and singularity are the only path to the top two catalog tiers — catalogUnlocked routes their req exclusively through hasPerkUnlock. The first migration is therefore the structural mid-game wall: you cannot brute-force into Petabyte files with Data alone.
DEV NOTE — perks compound multiplicatively with run upgrades because
perkMultfeeds the samedownMult/upMultGlobalproducts thatbandwidth/overclock/compressdo. Twodpilevels (×1.25²≈ ×1.56) carry across the wipe, so each migration starts from a higher floor even though local hardware is gone. That is the whole feel of prestige here — you lose the machine, you keep the protocol.
· · ·
Balance levers & if you change this
BASE_DOWN(1000) and thesqrtinclientDown()— the master pacing dial. Dropping thesqrtto a smaller exponent stretches every download; removing it makes late tiers either trivial or impossible. Touch this and re-derive the line table above before shipping.SAT_SEEDS(12) andperpeermult(1.3) — govern how punishing peer-limiting is and how fastperpeertrivializes it. RaisingSAT_SEEDSmakes the announce/peer loop matter for longer; raising themultlets a fewperpeerlevels obsolete it.- Catalog
size/up/seedsand the per-tier 10³–10⁴ gaps — the spine.maxUnlockedSize()readssizefor the line, so a new top entry silently raises everyone's ceiling. Keep new entries in tier order or the sqrt math skips rungs. creditsForexponent (0.42) and the 1e9 floor — prestige cadence. A higher exponent makes each migration worth more credits and shortens the perk grind; lowering the floor lets players prestige before they've meaningfully scaled, which devalues the run.seedboxFlat(+5e5/level, flat) vs. the multiplicative income chain — the flat addend is intentionally strongest on small files and early Private. If you make it scale, you erase the reason to prefer big seeders late-game.- Event tunables — burst ×10 / 8 s on a 45–90 s cadence; throttle ×0.4 / 9–16 s on 80–160 s.
quantumis a binary immunity. Rebalance the throttle and you change how badly the player needsquantumbefore Fiber. sanitizeState()is the safety net — any newGfield must be added there with a sane default, or a pre-update save will load it asundefinedand a tick will likely writeNaNintoG.data. Add the key tofreshStateandsanitizeStatetogether.