npm.io
0.1.0 • Published 7h agoCLI

rsl-licensing

Licence
MIT
Version
0.1.0
Deps
1
Size
40 kB
Vulns
0
Weekly
0

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-licensing
import { 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:, HTTP Link header, 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 stale

Generate 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

Keywords