Firmware dialects for the TP-Link router API.

A Dialect describes one firmware family's wire protocol as a flat table of pure functions — one request builder and one parser per protocol step. Dialects perform no I/O and hold no state: builders return a Request, parsers take a string or Headers. authenticate and execute own fetch, sequencing and crypto, and never branch on which dialect they hold.

Built-in dialects

Dialect Wire shape Models
gdprText text blocks over /cgi_gdpr TL-MR6400, Archer VR900v, TL-MR6500v, Archer MR600 v2
gdprJson JSON over /cgi_gdpr?9 EX220 — also TP-LINK NE200 and probably VX800v, both unconfirmed on hardware

Dialects are named by protocol shape, never by model, and there is no runtime model registry — a registry is exactly what a third party could not extend without editing this package. The model-to-dialect mapping above is documentation.

Authoring a dialect

Spread an existing dialect and override only what differs. Dialect has no optional members on purpose, so spreading is what supplies defaults.

Examples

Select a dialect for a router that speaks JSON

import { assertEquals } from "@std/assert";
import { gdprJson, gdprText } from "./mod.ts";

assertEquals(gdprText.defaultUsername, "admin");
assertEquals(gdprJson.defaultUsername, "user");

Derive a dialect for a model that differs in one place

import { assertEquals } from "@std/assert";
import { type Dialect, gdprJson } from "./mod.ts";

const vx800v: Dialect = {
  ...gdprJson,
  id: "vx800v",
  publicKeyRequest: (baseUrl) =>
    new Request(new URL("cgi/getParm", baseUrl), { method: "POST" }),
};

assertEquals(
  vx800v.publicKeyRequest("http://192.168.1.1").url,
  "http://192.168.1.1/cgi/getParm",
);
assertEquals(vx800v.defaultUsername, "user");

Interfaces

I
BusyStatus

Login and busy state reported by the router before authentication.

I
CommandBatch

One HTTP round trip's worth of actions.

I
Credentials

Credentials as supplied by the caller, after the dialect's username default has been applied.

I
DecodedBatch

Decoded result of one round trip, positionally aligned with CommandBatch.indices.

I
Dialect

A firmware family's wire protocol, expressed as pure functions and data.

I
Envelope

Encrypted request envelope: AES ciphertext plus the RSA-encrypted signature.

  • data: string

    Base64 AES-CBC ciphertext of the plaintext payload.

  • sign: string

    Hex RSA-encrypted parameter string: key&iv&h&s for login, h&s for commands.

I
PublicKeyInfo

RSA public key material and the request sequence base.

I
RouterInfo

Variables scraped from the router's login page.

I
SessionContext

Session material carried on every request made after login.

Type Aliases

T
T
ActionType = (ACT)[keyof ACT]

Numeric action type value from the ACT constant.

Variables

v
gdprJson: Dialect

EU/GDPR firmware speaking a JSON payload format over /cgi_gdpr?9.

v
gdprText: Dialect

EU/GDPR firmware speaking the bespoke text payload format over /cgi_gdpr.