Routeros Hotspot logo

Routeros Hotspot

Organization
tikoci
routeros-hotspot

RouterOS hotspot captive portal for wired/wireless access control. Use when: configuring hotspot on RouterOS, setting up captive portal, writing hotspot profiles or instances, configuring walled garden, setting DHCP option 114 (RFC 8910 captive portal URI), integrating RADIUS with hotspot, or when the user mentions /ip/hotspot, walled-garden, hotspot profile, or captive portal on MikroTik.

Overview

Publishertikoci
Repositoryrouteros-skills
Skill namerouteros-hotspot
Stars
85
Forks
18
Bundled files
2
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 2 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by tikoci on GitHub. Read the source before you install it.

Installation

Install the Routeros Hotspot AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/tikoci/routeros-skills/tree/main/routeros-hotspot
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/tikoci/routeros-skills.git /tmp/routeros-skills
mkdir -p .claude/skills
cp -r /tmp/routeros-skills/routeros-hotspot .claude/skills/routeros-hotspot
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Routeros Hotspot in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Routeros Hotspot on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Routeros Hotspot is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

RouterOS Hotspot

How Hotspot Chains Work

Hotspot traffic intercept runs before the regular firewall input/forward chains. This is the single most important fact to internalize:

  • /ip/hotspot binds to a bridge or interface — all traffic on that interface enters the hotspot chain first
  • Firewall rules blocking TCP 80/443 from the hotspot interface do NOT block the captive portal login page — hotspot handles it before the firewall sees it
  • RouterOS automatically injects dynamic firewall rules (hs-unauth, hs-auth chains) — do not manually create, remove, or interfere with these hotspot-managed rules

Common mistake: Adding a DROP rule for port 443 from bridge-hotspot to "fix a security gap" — this breaks the HTTPS login page silently.

Hotspot Profile

routeros
/ip/hotspot/profile/add \
  name=my-profile \
  hotspot-address=10.20.0.1 \
  login-by=https,mac,http-pap \
  mac-auth-mode=mac-as-username-and-password \
  dns-name=login.example.com \
  ssl-certificate=login.example.com.crt_0 \
  nas-port-type=ethernet \
  use-radius=yes \
  radius-accounting=yes \
  html-directory-override=hotspot-files

Key properties:

  • ssl-certificate= — reference the name after import (RouterOS appends _0 to imported certificate names)
  • nas-port-type= — use ethernet for wired hotspots and wireless-ieee-802-11-g for wireless hotspots
  • html-directory-override= — must match the exact folder name on the router's filesystem
  • login-by=https — serves the login page over HTTPS; requires www-ssl service enabled with the same certificate
  • use-radius=yes — when set, local /ip/hotspot/user entries are bypassed; adding them has no effect

Hotspot Instance

routeros
/ip/hotspot/add \
  name=hotspot1 \
  interface=bridge-hotspot \
  profile=my-profile \
  address-pool=pool-hotspot \
  addresses-per-mac=2 \
  idle-timeout=5m \
  keepalive-timeout=none \
  disabled=no

Note: keepalive-timeout=none disables the keepalive. keepalive-timeout=0 is NOT valid — it is ignored.

Note: RouterOS hotspot relies on NAT and is IPv4-only. IPv6 clients are not supported by the hotspot subsystem.

DHCP Option 114 — Captive Portal API (RFC 8910)

Option 114 (standardized 2020, RFC 8910) signals the captive portal URI to clients. It is underrepresented in LLM training data.

routeros
# force=yes is REQUIRED — without it, clients whose DHCP Parameter Request
# List does not include code 114 (e.g. iOS, Android) silently skip the option
/ip/dhcp-server/option/add \
  name=captive-portal \
  code=114 \
  force=yes \
  value="'https://login.example.com/api'"

/ip/dhcp-server/option/sets/add \
  name=captive-portal-set \
  options=captive-portal

/ip/dhcp-server/set my-dhcp-server dhcp-option-set=captive-portal-set

Option value syntax: outer double quotes, inner single quotes — "'https://...'". Missing inner quotes cause the option to be sent as a binary blob, not a string URI.

api.json timing: RouterOS creates hotspot/api.json only after the first client CAPPORT probe — not at hotspot enable time. Move it to the html-directory-override folder after first client connects:

routeros
# Handles both flash/ and non-flash storage layouts
:local srcPath "hotspot/api.json"
:local dstPath "hotspot-files/api.json"
:if ([:len [/file find name="flash"]] > 0) do={
  :set srcPath "flash/hotspot/api.json"
  :set dstPath "flash/hotspot-files/api.json"
}
:if ([:len [/file find name=$srcPath]] > 0) do={
  /file set [find name=$srcPath] name=$dstPath
} else={
  :log warning "api.json not yet created — run after first client CAPPORT probe"
}

Walled Garden

Use a consistent comment= tag for idempotent add/remove. Without it, repeated script runs accumulate duplicate entries.

routeros
# Remove only our entries, not manually-added ones
/ip/hotspot/walled-garden/ip/remove [find comment="my-wg"]

/ip/hotspot/walled-garden/ip/add dst-host=example.com   action=accept comment="my-wg"
/ip/hotspot/walled-garden/ip/add dst-host=*.example.com action=accept comment="my-wg"

IP vs HTTP walled garden:

  • /ip/hotspot/walled-garden/ip — layer 3 match by destination host, applied BEFORE authentication. Use for HTTPS destinations.
  • /ip/hotspot/walled-garden — layer 7 URL pattern match, requires HTTP. Does NOT work for HTTPS.

SSL Certificate Note

The hotspot profile references ssl-certificate=name.crt_0 (RouterOS appends _0 on import). Enable www-ssl with the same certificate:

routeros
/ip/service/set www-ssl disabled=no certificate=login.example.com.crt_0 tls-version=only-1.2

Quick import pattern:

routeros
/certificate import file-name=login.example.com.crt passphrase=""
/certificate import file-name=login.example.com.key passphrase=""
# After import, the certificate appears as login.example.com.crt_0

External Captive Portal — HTML Template Variables

RouterOS substitutes $(variable) server-side in any file under html-directory-override before serving it. These are not JavaScript variables — they are filled before the browser receives the page.

Most external captive portal setups override login.html, alogin.html, and status.html. Keep the full servlet page list, POST fields, RADIUS pass-through variables, multi-language target= mechanics, and HTTP response-control syntax in references/template-variables.md.

Use $(link-login-only) as the login POST endpoint, pass the original destination through dst, and always pick escaped variants such as $(link-orig-esc), $(mac-esc), and $(username-esc) when embedding values into URLs or query strings. The non-escaped variants can break parsing or open injection paths if a value contains &, =, ?, or #.

Conditional syntax (server-side, not JavaScript):

text
$(if logged-in == "yes")
  <a href="$(link-logout)">Logout</a>
$(else)
  <form action="$(link-login-only)" method="post">...</form>
$(endif)

Operators: presence ($(if VAR)), ==, !=. Also $(elif ...). No nested arithmetic / string ops.

Generic login.html pattern for external captive portal:

html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="pragma" content="no-cache">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- External auth provider loads redirect logic -->
  <script src='https://auth.example.com/snippets/router-os/redirect'></script>
</head>
<body>
  <div id="entryPoint">Redirecting...</div>
  <script>
    window.addEventListener('load', function () {
      ExternalAuth_redirect(
        '$(server-name)', '$(mac-esc)', '$(link-login-only)',
        '$(link-orig-esc)', '$(error-orig)', '$(logged-in)', '$(ip)'
      );
    });
  </script>
</body>
</html>

The external provider authenticates the user and redirects the browser back to $(link-login-only) with username + password + dst POST fields. RouterOS validates (local DB or RADIUS), issues the auth cookie, and redirects to dst.

Walled-garden must include the external auth host (and any CDN/asset hosts) — otherwise the unauthenticated browser cannot reach the redirect script in step 2.

Common LLM Mistakes

MistakeCorrect behavior
DROP TCP 443 from hotspot interfaceHotspot chain runs before firewall — breaks the HTTPS login page
keepalive-timeout=0 to disable keepaliveUse keepalive-timeout=none
Option 114 value= without inner single quotesMust be "'https://...'" — outer double, inner single
Option 114 without force=yesiOS/Android silently skip option if not in their DHCP PRL
Adding /ip/hotspot/user when use-radius=yesLocal users are bypassed when RADIUS is active
Wildcard HTTPS domains in /ip/hotspot/walled-gardenUse /ip/hotspot/walled-garden/ip for HTTPS (layer 3 match)
Hotspot on dual-stack network with IPv6Hotspot is IPv4-only — IPv6 not supported
$(link-login) treated as JavaScript variableIt is RouterOS server-side substitution, not JS
Embedding $(link-orig) / $(mac) / $(username) into another URLUse the -esc variant — non-esc breaks parsing on &, =, ? and may enable injection
Hotspot with PCC / multiple routing tablesHotspot uses only the default routing table — split-WAN setups need explicit /ip route rule for hotspot traffic

Additional Resources

Related skills:

  • routeros-fundamentals — RouterOS CLI syntax, REST API, scripting basics
  • routeros-certificates (in backlog) — for the full certificate chain handling pattern

MCP tools:

  • rosetta MCP server — /tool/ping, /ip/hotspot command tree inspection (routeros_search, routeros_get_page)

MikroTik docs:

RADIUS Integration

See references/radius-client.md for RADIUS client configuration patterns.

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Routeros Hotspot AI skill do?

RouterOS hotspot captive portal for wired/wireless access control. Use when: configuring hotspot on RouterOS, setting up captive portal, writing hotspot profiles or instances, configuring walled garden, setting DHCP option 114 (RFC 8910 captive portal URI), integrating RADIUS with hotspot, or when the user mentions /ip/hotspot, walled-garden, hotspot profile, or captive portal on MikroTik.

Why use Routeros Hotspot on TypingMind?

Because you install it once and use it with any model. Routeros Hotspot is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Routeros Hotspot in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/tikoci/routeros-skills/tree/main/routeros-hotspot. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Routeros Hotspot?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Routeros Hotspot?

As many as you like. As long as a model supports skills, you can use Routeros Hotspot with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Routeros Hotspot AI skill free?

Yes. It is published on GitHub by tikoci under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇