Websites

Create in-game websites accessible through the Chromo browser. Websites use HTML pages rendered inside sandboxed WebViews.

Basic Website

import { Website, RegisterWebsite } from "@hotbunny/hackhub-content-sdk";
import homePage from "./pages/home.html";
import aboutPage from "./pages/about.html";
import postsPage from "./pages/posts.html";

@RegisterWebsite
export class HackerForum extends Website {

    SiteName = "Hacker Forum";
    Host = "hackerforum.net";
    Icon = "./assets/forum-icon.png";
    Pages = [
        { path: "/", title: "Home", html: homePage },
        { path: "/about", title: "About", html: aboutPage },
        { path: "/posts", title: "Posts", html: postsPage },
    ];

}

Import HTML files directly - the build system bundles them as strings automatically.

Properties

Property Type Description
SiteName string Display name of the website
Host string Domain name (e.g. hackerforum.net)
Icon string Path to the website favicon
Pages (WebsitePageDefinition | DynamicWebsitePageDefinition)[] Array of page definitions
Popular boolean Show in the browser’s popular sites
Exports Record<string, any> Functions/values exposed to HTML

Pages

Each page in the Pages array has:

Property Type Description
path string URL path (e.g. /, /about, /products/:id)
title string Page title shown in the browser tab
html string HTML content (import from .html file)
seo boolean? If true, page appears in browser search results
description string? Description shown in search results
search string[]? Extra keywords for search ranking

Example with description and search keywords:

Pages = [
    {
        path: "/",
        title: "Hacker Forum - Home",
        description: "The underground community for elite hackers.",
        html: homePage,
        seo: true,
        search: ["hacking", "forum", "underground"],
    },
    {
        path: "/about",
        title: "About Us",
        description: "Learn more about Hacker Forum.",
        html: aboutPage,
        seo: true,
    },
];

Dynamic Pages

For pages with URL parameters (e.g. /products/:id), use DynamicWebsitePageDefinition with a metadata() function instead of static title/html:

import { Website, RegisterWebsite, DynamicWebsitePageDefinition } from "@hotbunny/hackhub-content-sdk";
import productPage from "./pages/product.html";
import homePage from "./pages/home.html";

@RegisterWebsite
export class MyShop extends Website {

    SiteName = "My Shop";
    Host = "myshop.mod";
    Icon = "";

    Pages = [
        { path: "/", title: "My Shop", html: homePage, seo: true },
        {
            path: "/products/:productId",
            seo: true,
            metadata(context) {
                const products: Record<string, { name: string; desc: string }> = {
                    "1": { name: "VPN Pro", desc: "Anonymous browsing tool" },
                    "2": { name: "CrackKit", desc: "Password recovery suite" },
                };
                const product = products[context.params.productId];
                if (!product) return null; // 404

                return {
                    title: `${product.name} - My Shop`,
                    description: product.desc,
                    html: productPage,
                    search: [product.name.toLowerCase()],
                    exports: { product },
                };
            },
        } satisfies DynamicWebsitePageDefinition,
    ];
}

PageContext

The metadata() function receives a context object:

Property Type Description
url string Full URL being navigated to
params Record<string, string> Route parameters (:productId → "1")
query Record<string, string> Query string parameters (?sort=price)
searchStr string? Search query (when called from Goagle search)

PageMetadata

Return an object describing the page, or null to show a 404:

Property Type Description
title string Page title
description string? Search result description
html string HTML content to render
search string[]? Dynamic search keywords
exports Record<string, any>? Values exposed as globals in the HTML

HTML Pages

Your HTML files are rendered inside sandboxed WebViews. You have full control over the page content:

<!DOCTYPE html>
<html>
<head>
    <style>
        body { background: #1a1a2e; color: #eee; font-family: monospace; }
        h1 { color: #0f0; }
    </style>
</head>
<body>
    <h1>Welcome to Hacker Forum</h1>
    <p>The underground community for elite hackers.</p>
</body>
</html>

Exports

Expose TypeScript functions and values to your HTML pages:

@RegisterWebsite
class MyWebsite extends Website {

    SiteName = "My Site";
    Host = "mysite.com";
    Icon = "./assets/icon.png";
    Pages = [
        { path: "/", title: "Home", html: homePage },
    ];

    Exports = {
        formatPost: (text: string) => text.toUpperCase(),
        siteVersion: "1.0.0",
    };

}

In your HTML, exported values are available as globals:

<script>
    const formatted = formatPost("hello world");
    document.getElementById("version").textContent = siteVersion;
</script>

Page Navigation

Links between your website’s pages work automatically. Use standard <a> tags with relative or absolute URLs:

<a href="/about">About Us</a>
<a href="https://hackerforum.net/posts">Posts</a>

Clicking a link will navigate the in-game browser to the correct page - no extra code needed.

For programmatic navigation, use the HackhubSDK.Browser API:

<script>
    // Navigate to a page on your site
    HackhubSDK.Browser.navigate("https://hackerforum.net/about");

    // Navigate to any website in the game
    HackhubSDK.Browser.navigate("https://goagle.com");
</script>
Method Description
Browser.navigate(url) Navigate the browser to the given URL
Browser.push(url) Push a new URL to the browser history
Browser.machine() { hostname, ip, user } of the computer the browser runs on over remote desktop, null on the player’s own

On someone else’s computer your page still runs with the player’s SDK. If Browser.machine() is not null, show the site signed out: no account details, balances or saved data. See Remote desktops.

SDK Access in HTML

All SDK APIs are available via the HackhubSDK global (Context Bridge):

<script>
    HackhubSDK.Mail.send({
        from: "[email protected]",
        subject: "Welcome",
        content: "Thanks for registering!",
    });

    HackhubSDK.Events.emit("MyMod.UserRegistered", { username: "hacker" });
</script>

See the WebView Context Bridge reference for the full list of available APIs.