Mova0.6 pre-release⌕
☰
home / docs / tools / Sea Spider — mova-seaspider

Sea Spider — mova-seaspider

Sea Spider is one of Mova's animals (Rust on Mova). It takes requests in English from the Octopus — a large language model (LLM) — or from Crab. See the sea animals of Mova.

Sea Spider is Mova's polite link downloader, named after the sea spiders (Pycnogonida), marine arthropods of every ocean: a Rust library and a CLI (seaspider). It fetches a queue of URLs as the abstract robot MovaBot, honours robots.txt, keeps at most one request per IP every 2 seconds, and stores raw pages compressed.

It does not run JavaScript, keep cookies, log in, submit forms or follow links unless asked.

seaspider serve takes arbitrary English prompts on a private socket, understands them with Mova's native deputy (tools/octopus) and answers in English, with the structured result alongside (see «In English» below).

Commands

seaspider fetch CONFIG QUEUE     fetch a queue, print a summary (JSON)
seaspider get STORE URL          write the latest stored body of URL (or sha256:HEX) to stdout
seaspider robots CONFIG URL      fetch robots.txt politely and explain the decision for URL
seaspider stats STORE            counts of the journal and segments (JSON)
seaspider serve CONFIG DEPUTY_CONFIG SOCKET   answer English prompts on a private socket (STOP ends it)
seaspider ask [--from CALLER] SOCKET ID [urgent] < text
                                 send arbitrary English; prints the English answer, then JSON

fetch, robots and serve take the singleton lock first (see «One Sea Spider per host»); while another Sea Spider holds it they print who runs and exit with status 5.

QUEUE has one URL per line: a JSON object with "url", or a TSV/plain line whose first field is the URL. Empty lines and # lines are skipped; duplicates are fetched once.

Build and test (standalone crate with its own workspace):

cargo build --release --manifest-path tools/seaspider/Cargo.toml --offline
cargo test --manifest-path tools/seaspider/Cargo.toml --offline

How it works

In English

seaspider serve CONFIG DEPUTY_CONFIG SOCKET runs Sea Spider as a persistent animal. The prompt is arbitrary English text plus an optional priority (urgent/normal); there is no command language. Mova's native deputy (mova_octopus::Deputy: Mova's annotator, the world tree reader and the configured capabilities) reads it; URLs and host names reach a capability only as typed arguments bound through the parse (see Crab's deputy planner (mova-octopus), «Typed arguments»). The answer is English realized deterministically (answer.rs over mova_octopus::realize: number agreement, plurals and past forms from Mova's morphology, lists, «I could not … because …» with the robots rule or HTTP status). No language model is involved.

Capabilities (handler seaspider, parameter operation; vocabulary in deputy.example.json):

operationslotexample promptsanswer
fetchurls: 1–16 URLs"Fetch a.example.", "Please fetch these pages: A, B", "Could you download A?"queued at once: «I have queued 2 pages for fetching (octopus-ID-0): A and B. 1 job is ahead of it.»
robotstarget: one URL or host"Are we allowed to fetch mova.elamur.ai?", "Can you read A?", "May we fetch A?"«Yes, …/robots.txt allows MovaBot to fetch A (rule Allow: /).» / «No, … disallows A for MovaBot (rule Disallow: /x).»
statshost: optional"How many pages have you stored?", "… stored from mova.elamur.ai?"«I have stored 3 pages from a.test. I could not fetch 1 more page: robots.txt disallowed 1.»
pageurl: one URL"What did you get from A?"«I fetched A at 2026-11-04 07:52 UTC: HTTP 200, text/html, 139 bytes. Its title is “Mova”. It links to 1 page.»
status—"What is the status of the current run?", "Is the queue empty?", "Are you busy?"«I am working on … : 0 of 1 page is done, now A. 2 jobs are waiting.» and the last job in the past tense

"Can …?" and "May …?" questions ask for permission and are answered from robots.txt; "Could you …?" and imperatives are requests. Only robots, stats, page and status (declared observes) answer questions: "Is URL fetched?" or "Did you fetch URL?" never fetches. A host target means https://HOST/. A refused prompt (negation, condition, unsupported verb such as "delete", no URL, two URLs where one is expected, a URL outside the requested action's parse, an invalid or non-public address) does nothing and answers «I did not act on this request because …», keeping a review candidate in the deputy's queue: teaching material, not a workaround.

One Sea Spider per host

Sea Spider is off by default: nothing starts it unless a deployment does (the public crab-worker.example.json has autostart: false). It is a singleton. serve, fetch and robots first take an exclusive flock on service.lock (default $XDG_STATE_HOME/mova/seaspider.lock, else $HOME/.local/state/mova/seaspider.lock) and write their pid, command and start time into it; a second start reads that, answers «Sea Spider is already running (pid N, serve SOCKET since …; lock …). I did not start a second one.» and exits with status 5, before it loads the deputy, opens the store or sends a request. The kernel releases the lock when the holder exits, so a crash leaves no stale lease. The holder runs a few threads (the socket server, the single fetch worker) but one fetch stream, so robots.txt and the per-IP timers are never split between processes that share the lock.

Who may ask

A request may name its caller in the envelope ("from": "crab"; seaspider ask --from crab); a request without one is the Octopus's. service.callers lists who is answered — default octopus, crab, whale and shark (a compatibility alias of whale, admitted by default for one release). Another caller gets «I did not act on this request because fish is not among the callers I answer (octopus, crab, whale, shark).» and nothing runs. requests.jsonl in the socket directory gets one fsynced line per request: id, caller, whether it was declared, the connecting pid and its executable (SO_PEERCRED, /proc/PID/exe), priority, the prompt's sha256 and whether it was admitted; queued jobs carry asked_by. The caller name is a declaration by a process of the socket's owner, not an authentication: the allowlist keeps animals to their roles; the 0700 directory and the owner check keep everyone else out.

Other animals use one shared client, mova_octopus::relay:

Configuration

JSON, unknown fields refused; relative store paths resolve against the configuration file. config.example.json lists every field with its default. Only robot.contact_url and store are required.

fielddefaultbound
robot.tokenMovaBot1–64 ASCII letters, -, _
robot.versioncrate version[A-Za-z0-9._-], ≤ 32
robot.contact_url—http(s) page, no @/userinfo/mailto
politeness.ip_interval_ms2000≥ 2000
politeness.max_crawl_delay_secs60≤ 86400
robots.ttl_secs864001–86400
robots.unreachable_retry_secs3600≥ 60
robots.max_redirects55–20
fetch.connect_timeout_ms, fetch.timeout_ms10000, 300001–300000
fetch.max_body_bytes10 MiB1 B–1 GiB
fetch.max_redirects5≤ 20
storage.segment_max_bytes256 MiB1 MiB–4 GiB
storage.zstd_level91–19
resume.refetch_after_secsnull (never)
follow.enabled / max_depth / max_pagesfalse / 1 / 1000depth ≤ 10
network.allow_private_addressesfalse
network.resolve{}host → IP overrides
network.dns_ttl_secs36001–86400
service.lock$XDG_STATE_HOME or $HOME/.local/state + /mova/seaspider.lockabsolute
service.callersoctopus, crab, whale, shark1–16 caller names

Checks

cargo test --manifest-path tools/seaspider/Cargo.toml --offline runs the unit, end-to-end, singleton CLI and English tests against a local HTTP server on 127.0.0.1 (no external network). They cover robots.txt matching and status handling, crawl-delay, the per-IP spacing, redirect re-checks, body, timeout and redirect bounds, the store and its crash repair, resume, links and follow mode, refusal of private addresses, the singleton lock and the caller allowlist. The English tests (tests/english.rs) need MOVA_OCTOPUS_TEST_MODEL set to a Mova model such as en.bin; without it they report a skip. Negative controls (for example dropping the per-IP booking, ignoring Disallow, admitting every caller) must turn the corresponding tests red.

Limits

Attribution

Adapted from an internal crawler: robots.rs (RFC 9309 matching), net.rs (per-IP next-free-moment scheduling), links.rs (the HTML link scanner, unchanged apart from English comments), store.rs (store v2 layout: zstd frames in append-only segments with a JSONL journal) and fetch/visit/robots of main.rs (bounded GET, manual redirects, robots.txt status handling). Not carried over: the internal crawler's consent levels and peer-crawler policy, CDN exception, address-pool harvesting and health bans, AIMD pacing, per-domain dictionaries and deltas, block store and dump, conditional requests. New in Sea Spider: the configurable MovaBot identity, the 2-second floor, the pinned resolver, redirect re-checks against the target origin, intent/uncertain records, deduplication by sha256, the reader, and follow mode.

This page in Markdown → https://md.mova.elamur.ai/free/en/docs/tools/seaspider/