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