
This is the biggest update Blackdoor's had since launch, and it's less about any one feature than about the world stopping being so still. A server used to be a filing cabinet - it sat there with its files and its users until you did something to it, and nothing happened in between. That's fine for a lot of the game, but it meant every "the system noticed you" moment was fake.
This update makes it real: servers run their own jobs now and react to what you're doing while you're still connected, not narrated after the fact. You can plant a script and walk away from it. You can get onto a box through FTP or a real database instead of just SSH. The factions that own those boxes are paying attention too, and can build a real case against you. You can dive into a permanent, player-built CTF pool alongside a new daily one. All of it's below, with the reasoning behind the bigger pieces spelled out alongside the specifics.
Every server now has its own background jobs, listening for real events and reacting to them on its own schedule - a nightly backup that actually runs, a monitoring sweep that actually fires, not a sentence in a text file pretending it does. list_jobs(conn) shows what's listening for what on a box you're connected to, emit_event(conn, name, payload) lets a script push a named event right now instead of waiting for it to happen naturally, and events in the terminal is the manual equivalent. This isn't limited to new content either - existing story and side missions past the earliest onboarding picked up real background jobs in the retrofit. If you're replaying something old, don't be surprised if a log looks a little different than you remember.
Thirteen new side missions and a new easter egg box are built entirely around this system - tracing an event chain across several logs, spoofing an event to open a locked door without tripping the alarm that would normally follow it, reconstructing a whole sequence of what happened from nothing but its own aftermath.
A server watching you closely enough can now do something about it in the moment, too: an IDS box polls ids_status(conn) for a quiet/flagged/active read on how worried it is about you, and once it's worried enough, it can rotate the account you just cracked, or slam a port shut under you mid-session. It won't happen everywhere - plenty of targets still don't have the budget or the staff to respond at all - but the ones that can, will.
Every firing, natural or forced, whether it ran or was skipped for insufficient privilege, gets a line in a new /var/log/jobs.log - it's not curated, so if an event chain didn't do what you expected, the receipt is right there instead of a guess.
daemon_listen(event, arg, callback) registers a function inside your running script as a persistent watcher, then the rest of the script just ends - free while it's idle, running entirely on the target's own resources, connected or not. Whenever that event fires again, only that one function wakes up and runs. daemons in the terminal (or daemons()/daemon_output(ip)/list_daemons(conn) from a script) shows what's planted, yours or someone else's, and Daemon Source ($800) shows you how the loop works underneath.
Miner Kit and Research Kit ($350 each) are the first real payoff - mine(kind) doesn't pay out in one call, it chips away at a chunk every time it runs, so a daemon has to loop it a few times before there's anything worth collecting, and deposit(kind) banks whatever's actually finished. A better box pays more per completed chunk, not faster chunks, which is exactly why specs (or server_specs(conn) from a script) is worth checking on anything you're thinking about planting on - it shows a connected target's real CPU and RAM tier, CPU deciding how fast anything running there actually executes, RAM deciding how many daemons it can host at once.
A few real standouts are worth hunting for: at least one genuinely enterprise-grade box, a couple of small/cheap ones that can barely host a single daemon, and boxes where the two specs deliberately don't match - a database server with weak CPU but serious RAM.
A server watching you closely enough eventually starts hunting for what you've left behind, too - once its IDS reaches the top "active" tier, it periodically sweeps for planted daemons. One you're not watching for gets removed outright; a script listening for on_ids_response gets one last dispatched run to react before it's gone.
Daemons feed back into everything above, too - planting one, losing one, or one of your own actually firing is a real event a server's own jobs can be listening for, the same as anything else that happens on that box. A remote_run() payload finishing pairs with the existing "started" event the same way.
You can hold more than one SSH connection open at once now - ssh a second box without losing the first, sessions lists and switches between everything that's open, and disconnect closes whichever one you're addressing without touching the rest. Get traced on one and every other open connection drops too, cleanly, no extra penalty for having more than one open.
That same capability is what makes two other new things possible: remote_run(conn, script_path) runs a script on a machine you've already pivoted onto rather than from home, its own connect() calls resolving as if it's genuinely sitting on that box (Pivot Source, $700, shows how it chains across hops with upload()); and certain internal servers now only answer while you're actually tunneled through the specific gateway that leads to them - lose that connection and the internal box goes dark again. nmap is a real command for finding those networks in the first place, reading whatever a connected box's own /etc/hosts, SSH config, and history genuinely reference, no fake subnet simulation - scan_network(conn) is the scripting equivalent.
FTP and real databases join SSH as ways in, each with their own vulnerability shape instead of being a copy of SSH's. FTP can be wide open to anonymous access, weakly credentialed, or just misconfigured - a broken chroot jail, a directory that'll take an upload from anyone (FTP Source, $500). Databases are real too - db_query(conn, sql) against actual tables and the occasional account with more privilege than whoever set it up meant to grant (DB Source, $600).
Both work straight from the terminal as well: ftp [user@]host drops you into a real ftp> prompt, and db [user@]host <password> does the same for db> - type SQL directly, no keyword prefix needed once you're in.
Scripts can now catch a runtime error instead of the whole thing halting on the spot: try: ... catch e: ..., with e.message/e.line telling you what actually went wrong. And two scripts - or a script and your own manual ssh session - genuinely don't step on each other's connection anymore. Before this update, whichever one called connect() most recently silently became the target for every other running script's next read_file()/crack()/whatever. Each script now resolves its own connection independently, the same way it always should have.
ctf easy/medium/hard/expert spins up that difficulty's box for today - three flags at increasing depth, something browsable with no credentials, something behind SSH, then the real challenge, buried in an FTP drop, sitting in a database, or needing genuine root depending on what the box turns out to be. submit <flag> banks each one as you find it, clearing all three pays reputation, and it resets at midnight UTC - same box for everyone attempting that difficulty that day.
You're not alone on it either: its auth.log fills in with other players who've attempted it today, their handles, their in-game IP, roughly when they showed up, and a new CTF Board tab on boards.blackdoor.tools tracks completions and times per difficulty.
On two of the three box layouts, the deepest flag doesn't just sit there waiting from the start - it becomes real a little while after you start poking at the box, so a daemon watching for it before you've dug through the easier flags will tell you the exact moment it's ready.
The part worth lingering on is that you can build one of these yourself. The Workshop editor gets a CTF-pool toggle - write your own flags into your own server, clear it yourself to prove it's actually solvable, and submit it. ctf community browses everyone else's; ctf community <box_id> attempts one, with your own randomly-generated flags the first time you tackle it.
Get played and you earn reputation for it, and holding the fastest clear time on a box - yours or anyone else's - keeps that reputation trickling in for as long as you hold the crown. The same editor pass that made this possible also lets new servers set a real CPU/RAM tier, FTP fields (anonymous login, jail root, traversal, writable dirs), and a full DB Users/DB Tables editor - the same authoring surface the official content above was built with, not a cut-down version of it.
Scripts can be edited in any external editor now, not just the built-in one. mount turns /mnt into a real folder on disk - drop .bd files in from whatever you actually use and they show up in-game within a couple of seconds. It's opt-in, and if you already have scripts in ~/scripts, it offers to copy them across the first time rather than leaving two divergent copies lying around. There's also a small local REST API behind it, for anyone building real tooling against the game - most players will never need it, mount is the command that matters day to day.
Reputation tops out at 100 now, and hitting it means something - you're marked "Ghost," shown right in the mission header and on the Highest Rep board.
Two new boards on boards.blackdoor.tools: Highest Rep and Most Money, same bragging-rights spirit as the existing time boards, reporting whenever you save or complete a contract.
The Missions and Network panes show a quick-glance stat line now - rep plus completed/open/locked counts on one, servers registered/scanned/cracked on the other. No new commands, just there whenever you look.
A handful of side servers hide something extra now, for absolutely no reason beyond fun - a database, an FTP drop, a mailbox worth actually checking on a box with zero mission relevance. Nothing to solve, nothing to unlock, just there if you go looking.
An admin's own routine command can be hijacked now, not just a structural event - find one in a bash_history, a job file, or their own notes, and daemon_listen() on that literal command name to get dispatched every time it fires again. daemon_stop() cleans up before anyone notices.
hexdump's audit contracts can ask for more than a filesystem grab or a web-form leak now - an anonymous FTP pull off a forgotten export drop, or a phishing line proving it'd actually land in someone's inbox. Same tools this update ships everywhere else, paying off in the repeatable contract board too.
store()/unstore() are real scripting functions now, not just terminal commands.
The terminal has real line-editing shortcuts - Ctrl+A/Ctrl+E jump to the start/end of the line, Ctrl+W deletes the last word (or path segment), Ctrl+D disconnects on an empty line, same as a real shell.
Any command now takes -h/--help for a one-line usage reminder.
You can define your own command shortcuts now with alias name=command - point it at a real command or one of your own scripts, whatever you type after the alias still gets passed through. alias alone lists what you've got, unalias name removes one, and a real command can't be shadowed.
Your own scripted functions can take default parameter values now - func f(a, b=10): works the same way built-in functions already do. Leave b out and it's 10, pass one and it overrides.
prompt("message") pauses a running script and reads a real line of typed input, mid-run - var answer = prompt("Continue? y/n") waits for you to actually type something, then keeps going with whatever you typed. Only works for a script running in the foreground, and only as its own statement, not buried inside a larger expression.
The Workshop editor can author real background jobs and emitters on a server now, not just official campaign content - the same event-reaction system behind a nightly backup or a monitoring sweep is something a community-built box can have too, straight from the editor, no hand-edited JSON required.
#lounge has real moderator tools now - right-click a name to mute or ban them if you've been granted moderator status, and a [MOD] tag marks who's actually holding it.
Some boxes have a port that isn't running a real service at all - netcat(ip, port) (or netcat <host> <port> from the terminal, recon only) hands back a task and a real countdown instead of a shell, too tight a window to answer by typing. Solving it for real means netcat_send(conn, response) from an actual script before time runs out - get it right and every other filtered port on that box opens up, get it wrong (or run out of time) and it costs you noise and a lockout on that port, so spamming reconnects isn't a real strategy. One new box out there exists purely to put this in front of you - no mission attached, just a hacker who built a training range and is happy to point you at it if you ask around.
SQL injection rewards real technique now, not just "type a quote and win." A vulnerable login form is backed by a real table - narrow it down with a column='value'-style filter, or pull only the columns you want with a real UNION SELECT - either one costs less noise than dumping the whole thing, and you can combine both. sqli()'s default payload is a real, working example now too, not an empty string, so it still teaches by example if you've never touched SQL injection before. The blunt dump-everything probe still works exactly like it always did, it's just not the only option any more. SQLKit ($300, shop) automates the whole thing - dumps the table, then tries a couple of targeted guesses and tells you honestly whether one actually landed narrower.
browse(url, visible=false) skips the pane swap for that call - useful for a script hitting many URLs in a loop, where the browser popping open every single time is disruptive rather than fun to watch. The page still loads for real and click()/fill_form()/submit_form() all work identically either way, it just doesn't pull focus. Leave the second argument off (or pass true) and it works exactly like it always has.
13 new Steam achievements, spanning daemons, multi-connection, FTP/DB, pivoting, CTF, try/catch, and mount.
The built-in code editor now supports editor=vim in ~/.config/blackdoor.conf, alongside the existing simple/nano options. Actual modal editing - hjkl/arrows/0/$/gg/G to move, i/a/o/O into Insert, x/dd/yy/p/P/u to edit, and a real : command line (:w/:q/:q!/:wq/:x) that refuses to :q on unsaved changes, same as the real thing.
Fixed IDS's disconnect-time noise penalty not accounting for an active proxy chain - it checked your real IP against a log that only ever shows your proxy's exit IP, so proxied disconnects never actually applied the penalty.
Fixed scripts being able to dodge CPU-tick cost just by writing an expression in a particular order - the same function call, wrapped in identical arithmetic, could cost wildly different amounts of tick budget depending only on which side of the expression it sat on - costs the same either way now.
Fixed mail and traces never tab-completing or typo-suggesting - real, documented commands that simply weren't in the completion list. Typing either during an su password prompt would also have been submitted as a literal password attempt instead of cancelling the prompt, unlike every other known command.
Fixed IDS's sweep (and scrub_log()) never actually seeing anything done through a script - crack(), connect(), su(), download()/upload() all wrote their log line to the file you can cat, but never to the record IDS itself checks, so a heavily-scripted session was invisible to IDS no matter how loud it should've been. The same gap meant lifetime crack stats didn't count typing a known password straight at the SSH prompt, or su-ing to a new account - only a script's crack()/su() did. Both fixed - manual and scripted play are tracked identically now.
Fixed several login pages missing a real <form> tag around their inputs, so a script's submit_form() had nothing to detect and submit - manually filling in the fields and clicking through in the browser worked fine the whole time, this only affected fully scripted play.
Fixed pivot() never feeding the same IDS sweep/attribution check disconnect() does - a script could crack a faction-owned server, leave unscrubbed log entries behind, and route through it with pivot() instead of disconnecting normally to dodge both the noise penalty and attribution entirely. pivot() now feeds the same check.
Fixed to_char() silently clamping anything above 127 to a fixed, invisible character instead of saying anything about it - calling it with a code above standard 7-bit ASCII quietly returned the exact same non-printing character every time. Widened to the full 0-255 byte range instead, matching to_code()'s own already-unrestricted inverse direction.
Fixed Workshop content resetting its visibility to public every time it was republished, even after you'd deliberately set it to friends-only or private.
Fixed a browse objective silently never completing when the page it wanted was only reachable by actually submitting a real login or signup form - it recorded a different version of the path internally than what the objective checked for, so doing it the real way never counted.
Various mission adjustments.
Thanks for playing, and for every bug report, feature idea, and "wait, that's not how it's supposed to work" message that came in along the way - a fair amount of what's in this update exists because someone bothered to say something. If Blackdoor's been worth your time, a review genuinely helps more than almost anything I can do myself from here - it's how people who'd actually like this find it. - Static