rsl-licensing
Emit valid RSL (Really Simple Licensing) AI-licensing terms from a JS/TS site — the machine-readable rules that say whether crawlers may train on, ground against, or index your content, and what they owe if they do.
RSL 1.0 shipped December 2025 with 1500+ publishers behind it (Reddit, Yahoo, Quora, O'Reilly, Medium, Vox, USA Today). The format is an XML vocabulary you advertise through robots.txt, an HTTP Link header, an HTML tag, RSS, or /.well-known. WordPress has a plugin. Until now JS/TS had nothing on npm — you hand-wrote the XML and hoped it matched the spec.
npm install rsl-licensingimport { rsl } from "rsl-licensing";
const b = rsl().maxAge(7);
b.content("/", { server: "https://api.example.com" })
.permits("usage", "search") // search engines: fine
.prohibits("usage", "ai-train") // training on this content: no
.payPerCrawl(0.015); // $0.015 per crawl if you want it
console.log(b.toXML());<?xml version="1.0" encoding="UTF-8"?>
<rsl xmlns="https://rslstandard.org/rsl" max-age="7">
<content url="/" server="https://api.example.com">
<license>
<permits type="usage">search</permits>
<prohibits type="usage">ai-train</prohibits>
<payment type="crawl">
<amount currency="USD">0.015</amount>
</payment>
</license>
</content>
</rsl>What it does
- Typed builder for the full RSL 1.0 vocabulary —
permits/prohibits(usage, user, geo),payment(crawl, training, use, attribution, subscription, …),legal,reporting. Autocomplete instead of XML by hand. - Validation against the 1.0 vocabulary, offline. Catches unknown usage tokens, bad payment types, the wrong namespace, and the spec's permit/prohibit conflict rule (prohibition wins). No network call, no XSD download.
- Embed across every carrier the spec defines: robots.txt
License:, HTTPLinkheader, HTML<link>and inline<script type="application/rsl+xml">, the RSS module, and/.well-known/rsl.xml. - Freshness check for CI — fail the build when your license is older than its own
max-age. - One dependency (
fast-xml-parser), ESM, ships its own types.
CLI
rsl validate <file.xml | -> Validate against the RSL 1.0 vocabulary
rsl build <config.json | -> JSON RSL document -> XML
rsl check <file.xml | url> Freshness: max-age vs content lastmod
rsl embed <file.xml> --as <carrier> [--url <licenseUrl>]
carrier = robots | link | html | script | rss | well-known
Validate in CI:
npx rsl validate public/.well-known/rsl.xml
npx rsl check https://yoursite.com/.well-known/rsl.xml # exits 1 when staleGenerate the robots.txt line and a Link header:
rsl embed license.xml --as robots --url https://yoursite.com/.well-known/rsl.xml
# License: https://yoursite.com/.well-known/rsl.xml
rsl embed license.xml --as link --url https://yoursite.com/.well-known/rsl.xml
# Link: <https://yoursite.com/.well-known/rsl.xml>; rel="license"; type="application/rsl+xml"Serving it
Host the XML at /.well-known/rsl.xml and reference it from the carriers above. The library hands you the body and the headers; wiring them into Next.js, Astro, Express, or a static build is a few lines:
import { rsl, wellKnown, linkHeader } from "rsl-licensing";
const b = rsl();
b.content("/").prohibits("usage", "ai-train").payPerCrawl(0.01);
const doc = b.build();
// e.g. a Next.js route handler
export function GET() {
const { body, contentType } = wellKnown(doc);
return new Response(body, { headers: { "content-type": contentType } });
}
// and the Link header on your HTML responses
const { name, value } = linkHeader("https://yoursite.com/.well-known/rsl.xml");API
| Export | Purpose |
|---|---|
rsl() |
Start a document builder |
.content(url, opts?) |
Add a <content> block; returns a chainable content builder |
.permits(type, ...tokens) / .prohibits(...) |
Add usage/user/geo rules |
.payment(type, opts) / .payPerCrawl(amount, currency?, standard?) |
Pricing |
.legal(type, value) |
Warranty / disclaimer / attestation / contact |
toXML(doc, opts?) |
Serialize a document object to RSL XML |
parseXML(xml) |
Parse RSL XML into a typed object |
validateXML(xml) / validateDocument(doc) |
Returns { valid, errors, warnings } |
robotsTxtDirective, linkHeader, htmlLinkTag, htmlScriptTag, rssModule, wellKnown |
Carrier emitters |
checkFreshness(xml, now?) |
max-age vs newest lastmod |
Scope and limits
This is the client-side half: generate, validate, and advertise terms. It does not run a license server, collect payments, or issue OAuth license tokens (the spec's OLP/CAP server protocols) — that's a hosted service the RSL Collective and others operate. payPerCrawl writes the terms into your document; enforcement and collection happen at your server or CDN.
Validation is structural — it checks the RSL 1.0 vocabulary, attributes, and conflict rules, which is what catches real authoring mistakes. It is not a full XSD schema validation.
License
MIT