Skip to content

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

typescript
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).

typescript
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.

typescript
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.

typescript
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

typescript
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()).

typescript
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.

typescript
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 ip argument can be a router IP or any child device IP behind it. Ports are resolved to the correct device, so nmap <ip> and Network.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.

typescript
// 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.

typescript
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.

typescript
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.

typescript
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.

typescript
const users = Network.createDefaultUserSchema(
    [Network.createUser({ username: "admin" })],
    { guest: true },
);
// Result: [{ username: "root" }, { username: "guest" }, { username: "admin" }]

Network.Type

Re-export of NetworkDeviceType enum:

typescript
enum NetworkDeviceType {
    Router = "ROUTER",
    Device = "DEVICE",
    Firewall = "FIREWALL",
    Splitter = "SPLITTER",
    Printer = "PRINTER",
}

Types

SubnetNetworkDefinition

typescript
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.files mount under that user's home directory (/home/<username>/...).
  • rootFiles mount 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.
typescript
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-game fern tool targets: running fern <model> finds the router with that model and recovers its password. If you leave model unset, the fern route 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 when accessable is true; otherwise it sends a refusal. This is the alternative to fern for 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).

typescript
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

typescript
interface NetworkPort {
    external: number;
    internal: number;
    active?: boolean;
    locked?: boolean;
    service?: string;
    version?: string;
}

NetworkUser

typescript
interface NetworkUser {
    username: string;
    password?: string;
    firstName?: string;
    lastName?: string;
    online?: boolean;
    files?: NetworkFileMap[];
    acceptReverseTCP?: boolean;
    email?: { address: string; password: string };
}

FirewallRule

typescript
interface FirewallRule {
    allowed: boolean;
    port: number;
    source?: string;
    destination?: string;
    locked?: boolean;
}

SubnetInfo

typescript
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

typescript
interface DomainInfo {
    domain: string;
    ip: string;
    subdomains?: string[];
}

FirewallInfo

typescript
interface FirewallInfo {
    ip: string;
    rules: FirewallRule[];
}

WifiNetwork

An access point as seen from the player's machine. Never carries the passphrase.

typescript
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

typescript
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:

typescript
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:

typescript
OnComplete() {
    Network.destroyNetwork("192.168.1.1");
}

HotBunny Interactive Entertainment Inc.