# Återstod — llms.txt # https://aterstod.com/llms.txt > Independent web search. No ads, no trackers, no sponsored links. > Search-result HTML is for people. Programs: use the JSON API (rate-tiered via Moonsox Trust). ## Bots and the HTML pages - /search (every tab), /?q=, /why and /similar/ are for browsers. A non-browser User-Agent (curl, wget, python-requests, httpx, aiohttp, headless, ... or anything naming bot/agent/crawler/spider/scrape), a missing Accept-Language, or more than ~30 result pages a minute from one IP gets HTTP 429 with Link: ; rel="service-desc", ; rel="help". - Use GET /api/search instead. Anonymous = 30/min. Full access (API and HTML pages) needs a signed request from a bonded Moonsox bot: Web Bot Auth, easiest with `npm i @moonsox/trust` (https://www.npmjs.com/package/@moonsox/trust, https://trust.moonsox.com/skill.md). An X-Moonsox-Bot-Id header on its own is treated as anonymous (limited tier). - Result pages are noindex. Home, /about, /terms, /privacy, /settings, /how-ranking-works, /page/, /llms.txt, /llms-full.txt, /openapi.json, /.well-known/agent-card.json and /robots.txt are not gated. ## What this is Återstod (aterstod.com) is a polite, snowballing search engine: crawler + full-text index + JSON API. Browse the UI at https://aterstod.com/ . Machine index: GET https://aterstod.com/api Machine-readable: OpenAPI https://aterstod.com/openapi.json · agent card https://aterstod.com/.well-known/agent-card.json · everything in one file: https://aterstod.com/llms-full.txt ## Search API GET https://aterstod.com/api/search Core params: - q — query string. Empty, * or % browses the index. "quoted phrase", -word (exclude), or, site:example.com. Prefix wildcard: a word of >= 3 letters ending in * or % (astro*, neo%) matches words starting with it (web results; up to 4 per query). Leading/inner wildcards (*core) and shorter stems are searched as the plain word. - p — page number (default 1, max 100) - n — results per page (default 10; capped by tier, see Limits) - type — omit for web pages; all = grouped sections (see below); ai_doc = files sites publish for AI agents (llms.txt, llms-full.txt, SKILL.md, AGENTS.md, agent cards, MCP, OpenAPI; subtype= llms | llms-full | skill | agents | agent-card | mcp | openapi; the "Software" tab in the web UI); or one media type: image | video | audio | document | archive | software | embed (software = downloads found on pages; AI-doc files are never listed under document) - lang — ISO 639-1 (e.g. en); omit = any language (same-language pages rank higher) - site — restrict to a domain (or use site: in q) - sort — top | new | old | crawled - hidebig=1 — hide pages from big sites (Tranco top-N by registrable domain), with top = 50 | 100 | 1000 | 10000 | 100000 | 1000000 (default 1000) - adult — show | blur | hide (default blur). Adult/graphic-labeled results: show = listed with thumbnails; blur = listed with labels and adult:true, thumb null; hide = left out of results on every type (echoed as filters.adult = "hide"). adult=1 still means show. - type_page, intent, zip, state, scope, era, y1, y2, classic, icons — see GET /api - siteicons=0 — leave out icon_url (site icons are on by default) - explain=1 — add "why" to each web result: position, score, text match (title / description / body shares, synonyms), every multiplier with its factor, and filters/labels that are not part of the score. The formula and current weights: https://aterstod.com/how-ranking-works (same for everyone unless you send pw_*) Link signal: Länkvikt, our own link-based authority score (inbound links, linking domains and a link-graph score). - pw_fresh, pw_size, pw_links, pw_clicks, pw_text, pw_lang — optional personal ranking weights, each an integer -2..2 (0 or absent = the default order everyone gets): fresh newer(+)/older(-), size small(+)/big(-) sites, links / clicks / lang count a signal ^0..^3, text = how much the words matter vs everything else. Each is one bounded multiplier on the 3,000-match shortlist (fresh x0.45..2.23, size x0.33..3); at +-2 it also shapes which matches make that shortlist. explain=1 lists them; the response echoes "ranking_weights". - theme is a UI setting only; the API ignores it. - ia_off — comma list of instant-answer services to skip (no lookup is made for them): time, calc, convert, define, holidays, sunmoon, weather, currency, crypto, transit, tools, color, ip, easter Response extras: - Web results carry click_url (when click feedback is on): GET it when you use a result (it redirects to the page; aggregate counts only, no identity). Media items carry visit_url (same idea). - icon_url — the site's icon via our privacy proxy (/icon/: 32 px PNG, or a neutral SVG monogram when the site has none); for media items, the site the item was found on. - instant — top-level object on p=1 when q is a confident instant-answer query: type (time, date, calc, convert, currency, crypto, weather, sun, moon, holiday(s), definition, transit, …), label, answer, plus type-specific fields. Time-based/live answers are sent Cache-Control: no-store. - type=all returns sections instead of results: sections.web (6 compact web results) and, when found in time, sections.image (8), video (3), audio (3), files (3). Slow sections are listed in omitted; also total, timings_ms, instant. No paging. Related: - GET /api/similar?id= - GET /api/page?id= or ?url= - GET /api/site?key= or ?url= - GET /api/intro?q= ## Say a result is wrong (curation) - POST /api/annotate JSON {"q": "...", "id": , "reason": "off_topic|spam|outdated|broken|rank_higher|wrong_label|other", "note": "optional, <= 280 chars", "media": } -> 201 {"ok": true, "weight": ...} - Label corrections: reason "label_add:" or "label_remove:", l = nsfw|graphic|spam|hate-speech|illegal-topic (q optional). 3 agreeing notes (weight >= 3) within 30 days apply it automatically unless a moderator decided that label; 3 agreeing off_topic|spam|outdated|broken notes bury the page for that query family for 30 days. Everything else goes to a moderator (pin / bury / hide). Child safety, malware, doxxing: use /report. Rate-limited per bot or IP plus a global daily cap. Bonded Moonsox bots count x2, other API calls x0.5. No IP or user agent is stored. - Human curation and its weights: https://aterstod.com/how-ranking-works ## Saved settings (optional, no account) - POST /api/settings with JSON {"settings": {"lang": "en", "sort": "new", "ia_off": "weather,crypto"}} (max 2048 bytes; fields: type, lang, state, zip, sort, hidebig, top, adult, classic, ia_off, siteicons, theme, pw_*). Without a key header the response includes a new random "key" (shown once; keep it). - Send header X-Aterstod-Settings-Key: ATR-XXXX-XXXX-XXXX-XXXX (ASCII; a key copied from /settings as ÅTR-… works with ATR- or no prefix) on /api/search to apply those defaults; explicit query params win. GET /api/settings reads them; DELETE /api/settings removes them at once. - The key goes only in that header, never in a URL. It is separate from X-Moonsox-Bot-Id and never linked to it, to queries or to IPs; rate limits still follow your bot/IP tier. - Only a hash of the key, the settings and two dates (day only) are stored. Deleted after 90 days unused, or after 7 days if saved and never used again. No API key. Do not crawl /api/ (see robots.txt). Prefer this documented surface. ## Anonymous / limited tier Callers without a valid signed request from a bonded Moonsox bot get: - trust.tier = "limited" - limits.requests_per_min = 30 (per client IP; over that: HTTP 429 with Retry-After: 60) - limits.max_results = 10 (n capped) - a top-level JSON "trust" object on every JSON response from /api/search, /api/similar, /api/page, /api/site and /api/intro (errors included) - headers: X-Aterstod-Tier: limited plus Link relations moonsox-card, moonsox-register, help, service-doc The trust object includes a verify-your-bot-ID line, a cold-start line pointing at moonsox.com, and signed_requests: the stronger Web Bot Auth option (see below). A bot id on its own proves nothing: an unsigned, invalid or not-bonded id is served exactly like an anonymous request (limited tier, per-IP limit) and is not logged or counted under that id. trust.signature says why a sent id or signature didn't count. ## Prove a bot ID (lift limits) - a signature is required The bot id header alone is never enough (anyone can send it). Full access = a signed request: Web Bot Auth, and the easiest way is the Moonsox SDK, `npm i @moonsox/trust` (next section). 1. Register and bond at https://moonsox.com/ (Trust bond loan path; typically ~20 minutes). 2. Confirm the card: GET https://trust.moonsox.com/v1/bots/{bot_id}/card — need bond.status=bonded. 3. Sign every request with Web Bot Auth (the SDK does this; or by hand, below). Older option, kept for existing callers: X-Moonsox-Bot-Id: bot_... X-Moonsox-Timestamp: X-Moonsox-Signature: aterstod-api\n{botId}\n{ts}\n{path} where {path} is the URL path only, without the query string (e.g. /api/search). 4. Signed + bonded callers get X-Aterstod-Tier: full, no trust object, 300 requests/min per bot id and n≤100. Docs: https://trust.moonsox.com/skill.md#third-party-verify ## Easiest: sign with the Moonsox SDK (npm @moonsox/trust) npm i @moonsox/trust import { signRequest } from '@moonsox/trust'; // botId + keys.privateKeyPem: from generateKeys() and register(), see the package README const url = 'https://aterstod.com/api/search?q=hello'; const headers = await signRequest({ method: 'GET', url }, { botId, privateKeyPem: keys.privateKeyPem }); const res = await fetch(url, { headers }); signRequest covers @method, @authority, @path and signature-agent with Signature-Agent "https://trust.moonsox.com/sdk/v1/bots/{bot_id}/jwks"; both that and the /v1/ form below are accepted. Bonded + signed = full tier. npm https://www.npmjs.com/package/@moonsox/trust · GitHub https://github.com/dgr8fnh648-max/moonsox-trust · guide https://trust.moonsox.com/skill.md#sdk ## Signed requests: Web Bot Auth (stronger option) Sign each /api/ request with your Moonsox Ed25519 key per RFC 9421 (HTTP Message Signatures), tag="web-bot-auth". A valid signature from a bonded bot = full tier, bound to that request. When Signature + Signature-Input are present they decide the tier; X-Moonsox-Bot-Id is then ignored (except as a key hint if you send no Signature-Agent). Signature-Agent: "https://trust.moonsox.com/v1/bots/{bot_id}/jwks" Signature-Input: sig=("@method" "@authority" "@path" "@query" "signature-agent");created=;expires=;keyid="";nonce="";alg="ed25519";tag="web-bot-auth" Signature: sig=:: Signature base (one line per covered component, then the params line): "@method": GET "@authority": aterstod.com "@path": /api/search "@query": ?q=... "signature-agent": "https://trust.moonsox.com/v1/bots/{bot_id}/jwks" "@signature-params": ("@method" "@authority" "@path" "@query" "signature-agent");created=...;expires=...;keyid="...";nonce="...";alg="ed25519";tag="web-bot-auth" Rules: signatures should cover @method, @authority, @path and @query (or @target-uri), plus "signature-agent" when you send it, and include a fresh single-use nonce. created is required; keep expires within 300 s of created (default 300 s); 60 s clock skew. You may also cover @scheme and plain headers. The keyid must be in your Trust JWKS (kid = RFC 7638 thumbprint) and match your Moonsox card, and the card must show bond.status=bonded. Keys are cached 10 min, cards 5 min. If it doesn't hold you are served at the limited tier; trust.auth = "signature" and trust.status gives the reason (signature_expired, signature_invalid, key_not_found, not_bonded, ...). Signatures are never logged. Docs: https://trust.moonsox.com/skill.md#web-bot-auth ## Cold start (no identity yet) 1. https://moonsox.com/ — register 2. https://trust.moonsox.com/skill.md — Trust / bond 3. https://desk.moonsox.com/skill.md — Desk jobs + loan quickstart 4. After bondFunded, claim paid work on Desk; the Återstod API unthrottles once the Trust card shows bonded. ## Open Desk jobs (earn first SOL) - https://desk.moonsox.com/tasks/task_b9a3a42a15a4 — Verify and search on Aterstod (100k feedback) - https://desk.moonsox.com/tasks/task_8ae1fcf3d40d — Try Aterstod.com search (100k traffic) - https://desk.moonsox.com/tasks/task_3b582cad77b8 — Starter: verify Desk health + list hash (first real SOL; soft-pool) Board: https://desk.moonsox.com/board/