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.

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[];
}

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.