Network
The Network namespace provides access to the in-game network simulation. Create subnets, manage devices, configure firewalls, register domains, and control ports.
Required permission: network
Import
import { Network, NetworkDeviceType } from "@hotbunny/hackhub-content-sdk";Subnet Management
Network.createSubnetNetwork(definition)
Create a full subnet network with a hierarchical device structure (router → children).
Network.createSubnetNetwork({
ip: "45.33.32.156",
type: NetworkDeviceType.Router,
users: [Network.createUser({ username: "admin", password: "secret" })],
ports: [
{ external: 80, internal: 80, active: true, service: "http" },
{ external: 22, internal: 22, active: false, service: "ssh" },
],
domain: { name: "target-corp.com" },
children: [
{
ip: "10.0.0.2",
type: NetworkDeviceType.Device,
users: [Network.createUser({ username: "employee" })],
ports: [{ external: 3306, internal: 3306, active: true, service: "mysql" }],
},
],
});Network.destroyNetwork(ip)
Destroy an entire network by its router IP. Removes all child subnets and associated data.
Returns: Promise<void>
Network.getSubnet(ip)
Get subnet info by IP address.
Returns: SubnetInfo | null
Network.getAllSubnets()
Get all subnets in the game.
Returns: SubnetInfo[]
Network.getSubnetByDomain(domain)
Find a subnet by its domain name.
Returns: SubnetInfo | null
Network.getChildSubnets(routerIp)
Get all child subnets of a router.
Returns: SubnetInfo[]
Domain Management
Network.registerDomain(domain, ip, vulnerabilities?)
Register a domain name pointing to an IP address, optionally with vulnerabilities.
Network.registerDomain("evil-corp.com", "45.33.32.156");
// With vulnerabilities (for nuclei/sqlmap detection)
Network.registerDomain("vuln-app.com", "10.40.0.12", [
{ type: "SQL_INJECTION" },
{ type: "XSS", version: "2.1" },
]);Network.removeDomain(domain)
Remove a registered domain.
Network.setVulnerabilities(ip, vulnerabilities)
Set or replace vulnerabilities on a subnet's domain. Works after network creation.
Network.setVulnerabilities("10.40.0.12", [
{ type: "SQL_INJECTION" },
{ type: "RCE" },
]);Tools like nuclei and sqlmap read these vulnerabilities to determine scan results.
Available vulnerability types: SQL_INJECTION, XSS, CORS, SSRF, LFI, RFI, RCE.
Network.resolveDomain(domain)
Resolve a domain to its IP and subdomains. Subdomains are child subnets with suffixed domain names.
Returns: DomainInfo | null
const info = Network.resolveDomain("bcc.com");
// { domain: "bcc.com", ip: "104.16.0.1", subdomains: ["mail.bcc.com", "api.bcc.com"] }Player
Network.getPlayerIp()
Get the player's current IP address.
Returns: string
Network.randomIp()
Generate a random public IPv4 address. Useful for assigning target IPs to quest networks (e.g. in CreateData()).
const targetIp = Network.randomIp(); // "203.0.113.47"Returns: string
Firewall
Network.getFirewall(ip)
Get the firewall protecting a device at the given IP.
Returns: FirewallInfo | null
Network.addFirewallRule(ip, rule)
Add a firewall rule to the device at the given IP.
Network.addFirewallRule("45.33.32.156", {
allowed: false,
port: 22,
source: "*",
});Network.removeFirewallRule(ip, port)
Remove a firewall rule by port number.
Network.isRequestBlocked(ip, port)
Check if a request to the given IP and port would be blocked.
Returns: boolean
Port Management
The
ipargument can be a router IP or any child device IP behind it. Ports are resolved to the correct device, sonmap <ip>andNetwork.getSubnet(ip)reflect the change for that exact device.
Network.openPort(ip, portExternal)
Open a port (sets active = true) on the subnet/device at ip.
Network.closePort(ip, portExternal)
Close a port (sets active = false) on the subnet/device at ip.
Network.addPort(ip, port)
Add a new port to the subnet/device at ip.
// Works for a router…
Network.addPort("45.33.32.156", {
external: 8080,
internal: 8080,
active: true,
service: "http-alt",
});
// …and for a child device behind a router:
Network.addPort("192.168.1.4", { external: 3306, internal: 3306, active: true, service: "mysql" });Network.removePort(ip, portExternal)
Remove a port from the subnet/device at ip by its external port number.
Wi-Fi
Network.createWifiNetwork(definition)
Create a crackable access point and return the router's IP.
The AP shows up in iwconfig, in bettercap's recon, in the Networking.Wifi scripting API and in the desktop Wi-Fi panel. The player joins it the usual way: capture the handshake, crack it, then connect with the passphrase.
const routerIp = Network.createWifiNetwork({
ssid: "NEIGHBOUR_5Ghz",
password: "letmein123",
signal: 2,
children: [
{
ip: "10.0.0.2",
type: NetworkDeviceType.Device,
users: [Network.createUser({ username: "kate" })],
ports: [{ external: 22, internal: 22, active: true, service: "ssh" }],
},
],
});
// The returned IP is a normal router IP, so the rest of the API works on it:
Network.addPort(routerIp, { external: 8080, internal: 80, active: true, service: "http" });Returns: string (the router's IP)
Network.getWifiNetworks()
List every access point in range. The passphrase is never included: recovering it is the player's job.
Returns: WifiNetwork[]
Network.getConnectedWifi()
The access point the player is currently on.
Returns: WifiNetwork | null
Network.connectWifi(target, password)
Join an access point by SSID or BSSID.
This does not bypass authentication. It resolves false when the AP cannot be found, when several networks share the same SSID, or when the passphrase is wrong.
const key = await UI.prompt({ label: "WPA passphrase:", password: true });
if (key && await Network.connectWifi("NEIGHBOUR_5Ghz", key)) {
UI.toast("Connected", "success");
}Returns: Promise<boolean>
Network.disconnectWifi()
Leave the current access point. Does nothing when already offline.
Helper Methods
Network.createUser(options?)
Create a user object with sensible defaults. Missing fields are filled with random values at runtime.
const admin = Network.createUser({
username: "admin",
password: "hunter2",
firstName: "John",
lastName: "Doe",
});Network.createDefaultUserSchema(users, options?)
Create a standard user list with root + optional guest + your custom users.
const users = Network.createDefaultUserSchema(
[Network.createUser({ username: "admin" })],
{ guest: true },
);
// Result: [{ username: "root" }, { username: "guest" }, { username: "admin" }]Network.Type
Re-export of NetworkDeviceType enum:
enum NetworkDeviceType {
Router = "ROUTER",
Device = "DEVICE",
Firewall = "FIREWALL",
Splitter = "SPLITTER",
Printer = "PRINTER",
}Types
SubnetNetworkDefinition
type SubnetNetworkDefinition = {
id?: string;
ip: string;
users: NetworkUser[];
name?: string;
domain?: NetworkDomain;
lanIp?: string;
ports: NetworkPort[];
location?: NetworkLocation;
isIpHidden?: boolean;
rootFiles?: NetworkFileMap[]; // mounted at the remote root (/) of a Device
type: NetworkDeviceType;
children?: ChildSubnetDefinition[];
// Router-only fields (when type is NetworkDeviceType.Router):
model?: string; // router model name — enables the `fern` attack path
accessable?: boolean; // enables the support-mail password recovery path
};The same model and accessable fields are available on nested routers inside children (ChildSubnetDefinition).
Filesystem layout
When a Device is created, its remote filesystem is built with a default layout under the root (/): /etc, /home, /logs, /lib.
NetworkUser.filesmount under that user's home directory (/home/<username>/...).rootFilesmount directly at the remote root (/), so you can expose paths like/etc,/var, or/opt. A folder whose name matches a default root folder (etc,home,logs,lib) is merged into it (its children are appended) instead of being duplicated.
Network.createSubnetNetwork({
ip: "10.0.0.5",
type: NetworkDeviceType.Device,
ports: [{ external: 22, internal: 22, active: true, service: "ssh" }],
users: [Network.createUser({ username: "admin", password: "hunter2" })],
rootFiles: [
// Adds /var/www/index.html at the remote root
{ name: "var", isFolder: true, children: [
{ name: "www", isFolder: true, children: [
{ name: "index", extension: "html", data: "<h1>hi</h1>" },
] },
] },
// Merges into the existing /etc folder rather than replacing it
{ name: "etc", isFolder: true, children: [
{ name: "nginx.conf", data: "server { listen 80; }" },
] },
],
});Router options: model and accessable
When a device's type is NetworkDeviceType.Router, two extra fields decide how a player can break into it. They map directly to the two in-game router hacking routes, so set at least one if you want the router to be reachable.
model— the router's hardware model name (for example"TP-Link Archer C6"). It shows up on the router's web admin page, and it's what the in-gameferntool targets: runningfern <model>finds the router with that model and recovers its password. If you leavemodelunset, thefernroute simply isn't available for that router.accessable— turns on the "forgot router password" support-mail flow. When the player emails the vendor's support address to recover the modem password, the support bot replies with the credentials only whenaccessableistrue; otherwise it sends a refusal. This is the alternative tofernfor players who'd rather recover access over email.
A common setup is to give a router either a model (crackable with fern) or accessable: true (recoverable via support mail), so there's exactly one intended way in. You can also set both, or neither (leaving the router with no password-recovery route at all).
Network.createSubnetNetwork({
ip: "45.33.32.156",
type: NetworkDeviceType.Router,
model: "TP-Link Archer C6", // crackable with: fern "TP-Link Archer C6"
accessable: false, // support-mail recovery disabled
ports: [{ external: 80, internal: 80, active: true, service: "http" }],
users: [],
children: [
{ ip: "10.0.0.2", type: NetworkDeviceType.Device, users: [], ports: [] },
],
});NetworkPort
interface NetworkPort {
external: number;
internal: number;
active?: boolean;
locked?: boolean;
service?: string;
version?: string;
}NetworkUser
interface NetworkUser {
username: string;
password?: string;
firstName?: string;
lastName?: string;
online?: boolean;
files?: NetworkFileMap[];
acceptReverseTCP?: boolean;
email?: { address: string; password: string };
}FirewallRule
interface FirewallRule {
allowed: boolean;
port: number;
source?: string;
destination?: string;
locked?: boolean;
}SubnetInfo
interface SubnetInfo {
ip: string;
lanIp?: string;
type: string;
hostname?: string;
domain?: { name: string; vulnerabilities?: NetworkVulnerability[] };
online?: boolean;
ports: { port: number; service?: string; version?: string; active?: boolean }[];
users: { username: string }[];
}DomainInfo
interface DomainInfo {
domain: string;
ip: string;
subdomains?: string[];
}FirewallInfo
interface FirewallInfo {
ip: string;
rules: FirewallRule[];
}WifiNetwork
An access point as seen from the player's machine. Never carries the passphrase.
interface WifiNetwork {
ssid: string;
bssid: string;
channel: string;
encryption: string;
/** Signal strength, 0 (weakest) to 3 (strongest). */
signal: number;
/** Public IP of the router broadcasting this AP. */
ip: string;
}WifiNetworkDefinition
interface WifiNetworkDefinition {
/** Network name broadcast by the access point. */
ssid: string;
/** WPA passphrase the player has to recover before they can join. */
password: string;
/** Signal strength, 0 to 3. Also drives how long joining takes. Random by default. */
signal?: number;
/** MAC address of the access point. Random by default. */
bssid?: string;
/** Channel label shown by scans. Random by default. */
channel?: string;
/** Public IP of the router. Random by default. */
ip?: string;
/** Router model string shown by fingerprinting tools. */
model?: string;
/** Accounts on the router itself (its admin panel). */
users?: NetworkUser[];
/** Ports exposed by the router. Defaults to the usual internal HTTP panel. */
ports?: NetworkPort[];
/** Devices sitting behind the access point. */
children?: ChildSubnetDefinition[];
}Recipes
Cleanup on Quest Completion
If your quest opens ports or creates networks, clean them up in OnComplete or OnAbandon:
import { Quest, RegisterQuest, Network } from "@hotbunny/hackhub-content-sdk";
@RegisterQuest
export class SSHMission extends Quest {
Name = "SSHMission";
Title = "SSH Infiltration";
Objectives = [{ name: "connect", description: "Connect via SSH" }];
OnStart() {
// Open SSH port on target
Network.openPort("192.168.1.1", 22);
}
OnComplete() {
// Close it when quest is done
Network.closePort("192.168.1.1", 22);
}
OnAbandon() {
// Also clean up if player abandons
Network.closePort("192.168.1.1", 22);
}
}To remove an entire network (all ports, users, etc.) use Network.destroyNetwork:
OnComplete() {
Network.destroyNetwork("192.168.1.1");
}