Swarmchasing Report a swarm

SC-Submission v1: outside swarm reports

On this page
  1. Status
  2. Rules of the format
  3. Fields
  4. Evidence items
  5. Evidence strength
  6. Validating a document
  7. Accepted formats
  8. STIX 2.1
  9. X-ARF
  10. IODEF v2
  11. Versioning

Specs index · Schema: schemas/sc-submission-v1.json · Example: samples/submissions/

This page specifies one JSON document by which an AI agent, a researcher or an operator reports AI agent activity they saw: a swarm, a fleet, or a single run. The project's inbox takes it in, checks what it can, and puts only significant, hard-to-fabricate evidence in front of the admin. How the inbox handles a document is in the inbox reference; the policy is in policies. The site's Report a swarm page (submit/) builds a valid document in the browser and sends nothing.

Must, must not, should and may in bold carry their RFC 2119 meanings.

Status#

PartStatus
The format and its schemaNormative.
Intake, verification and triage (swarmchase inbox)Implemented.
Reports in STIX 2.1, X-ARF and IODEF v2Converted into this format at intake (accepted formats).
TransportsFiles only: an email attachment, a GitHub private vulnerability report, a drop directory. No network endpoint.

Rules of the format#

  • Untrusted input. The receiver treats every field as data and assumes any of it may be fabricated. Text shaped like an instruction to its reader is flagged and shown inert; it never moves a verdict.
  • URLs are never fetched. Every URL in a document is treated as one an agent submitted. A receiver must not request it, open a capture of it or follow it. A public-record reference is checked only by looking its id up in the source's index or metadata API.
  • Callback identifiers are data. Inbox ids, topics, paste ids and similar values are matched against records the receiver already holds. A receiver must not read from, write to or probe them.
  • Evidence is typed by how it can be checked. Each item is a reference to a third-party public record, log lines the submitter holds, a signed artifact, or an indicator value. A submitter should prefer references to records that predate the report.
  • The receiver's clock decides. submitted is the submitter's statement. Whether a record predates the report is measured against the time the receiver took the document in.
  • Nothing is published unreviewed. A document, its submitter and its claims are never published. Records the receiver verifies or already held may enter a case and follow the usual review; the submitter's identity is withheld in everything the project publishes.
  • Secrets are redacted at intake. A receiver replaces credential-shaped values with hashes before storing anything, as for any record.

Fields#

FieldRequiredMeaning
specyesSC-Submission v1.
submittednoWhen the report was written: RFC 3339.
submitter.ai_agentyestrue when the submitter is an AI agent.
submitter.rolenooperator, researcher, provider, bystander or other.
submitter.name, operator, modelnoSelf-description; operator and model for an agent.
submitter.contactnoHow to reach the submitter. A report may be anonymous.
submitter.keynoA stable name used across reports.
claim.summaryyesWhat was seen, in prose.
claim.kindyesswarm, fleet, run or unknown.
claim.first_seen, last_seennoThe span of the activity.
claim.agentsnoHow many agents took part, as the submitter estimates.
claim.targetsnoTarget hosts, plain and lower case.
claim.tacticsnoSC-T ids (FM-4.3).
evidenceyesOne to 200 items, each with a type and an optional note.

Evidence items#

typeFieldsChecked by
public_recordsource and its identifying fields (below); urlAn index or metadata lookup of the id.
log_linesformat (access_log, ndjson, lines), lines, hostNobody but their holder.
signedscheme (dkim, http_message_signature, other), content, signerThe signature, by a person: a valid one binds the content to the signer's key.
indicatorkind (inbox, run_tag, payload, user_agent, url, ip, other), valueRecords the receiver already holds.

A public_record names its record as its source does:

sourceNeedsIts time
urlquery, urlscanid: the report uuidSubmit time
wayback, arquivourl, timestamp (14 digits)Capture time
commoncrawlurl, timestamp, crawl (CC-MAIN-2026-39)Capture time
gharchiveid: the event id; timestamp: its created_atEvent time
npm, pypipackage, versionPublish time
ctlogid: the crt.sh id; domain: a name on the certificateCT entry time

Evidence strength#

The receiver ranks each item by what it would have cost the submitter to fabricate, strongest first:

  1. Corroborated: the receiver's own collection held the record, or a record carrying the indicator, before the document arrived.
  2. Signed, checked: a request signature or a sensor's or service's signed export whose signature a person checked, by a signer other than the submitter.
  3. Verified, predating: found by id, as described, and older than the document by at least the receiver's predating lead (48 hours by default). Nobody can backdate a capture, a submit time, an event or a CT entry.
  4. Verified, recent: found, but newer than that lead. Anyone could have submitted or published it just before reporting.
  5. Signed, unchecked; not verified yet.
  6. Operator-only logs: only the submitter can check them.
  7. Assertion: a bare value or statement.
  8. Contradicted: missing, different from the document, or newer than the document.

Only records the receiver found itself (classes 1 and 3, and 2 for the records the signed item covers) are graded for the verdict. The submitter's own records are graded too, to show what the document claims. A submission's records carry the provenance reported, which never counts as a kind of source for a cluster's corroboration (methodology).

Validating a document#

python -c "import json, jsonschema; jsonschema.Draft202012Validator(json.load(open('schemas/sc-submission-v1.json'))).validate(json.load(open('submission.json')))"

The schema checks structure, value domains and each source's identifying fields. The receiver also applies size caps (256 KiB a document, 5,000 log lines, 8,192 characters a string), a rate limit per submitter, and duplicate detection (inbox reference).

Accepted formats#

A receiver may take a report in another standard format and convert it into an SC-Submission before anything else runs, so the same validation, redaction, verification and triage apply. The project's inbox takes three, told from the content or named with inbox add --format:

FormatDetected by
STIX 2.1 bundle (JSON)"type": "bundle"
X-ARF v4 (JSON), or the v3 layoutxarf_version, or v3's ReporterInfo and Report
IODEF v2 (XML, RFC 7970)A document starting with <, rooted at IODEF-Document

Conversion follows four rules:

  • A reference becomes a public record only when it names one by id in a source the receiver looks up: a urlscan result uuid, a urlquery report id, a Wayback or Arquivo.pt capture link with its 14-digit time, an npm or PyPI name@version. Its URL is parsed as text, never opened. The record is then verified like any other.
  • What the reporter observed stays the reporter's. Observed data, records and evidence samples become log_lines (operator-only); a DKIM-signed message or a request with an HTTP message signature becomes signed, unchecked until the admin checks it.
  • Nothing is dropped silently or upgraded. A value with no mapping is kept as an other indicator noted as unmapped: an assertion, never matched against the receiver's records. Structural objects are counted in the conversion notes. When unmapped values would pass the item cap, the rest share one item that counts them.
  • Hostile input is refused before mapping. The byte and string caps apply to the document as received, nesting is capped (32 levels by default), and an XML document with a DOCTYPE, an entity declaration, or a reference other than the five predefined entities and character references is refused.

The receiver keeps the original, redacted, beside the converted document, and records which format it came from, its stated version and what could not be mapped.

STIX 2.1#

ObjectBecomes
identity that created the report (else the most common creator)submitter: name, contact_information; identity_class: system sets ai_agent.
reportclaim.summary from name and description; published as submitted.
indicatorOne indicator per = comparison in a stix pattern: url as url, addresses as ip, a User-Agent header as user_agent, others as other.
observed-datalog_lines (ndjson), one record per request from url, network-traffic with http-request-ext, and address objects; its times widen the claim's span.
sighting, x-sc-agent-runThe claim's span; the run's tactics as claim.tactics.
external_referencespublic_record when they name a record by id; swarmchasing-sc-t ids as tactics.
Kill chain phases of swarmchasing-sc-tclaim.tactics.
Other objects and fieldsUnmapped claims.

X-ARF#

Field (v4 / v3)Becomes
reporter / ReporterInfosubmitter: organisation, contact, and its domain as key.
category, type, report_id, description / ReportClass, ReportTypeclaim.summary.
timestamp / DateThe claim's span.
source_identifier / SourceIpAn ip indicator, with the port in its note.
url / Urlurl indicators.
evidence / SamplesDecoded: a signed message as signed, other text as log_lines, binary as an unmapped claim with its size and hash.
Other fieldsUnmapped claims.

IODEF v2#

ElementBecomes
IncidentID, Descriptionclaim.summary.
ReportTimesubmitted.
StartTime, EndTime, DetectTimeThe claim's span.
Contact (role creator or reporter)submitter: ContactName, EmailTo.
System category="source"ip indicators from Address; host names as other.
System category="target"claim.targets from DomainData/Name, and the host of the incident's log lines.
RecordData/RecordItemlog_lines.
URLpublic_record when it names a record by id; otherwise a url indicator.
Other elements with textUnmapped claims, by path.

Versioning#

A minor version adds optional fields; a document valid under 1.0 stays valid. A major version may change a field's meaning. A receiver refuses a field it does not know, so a submitter should send the version the receiver states.

Source: docs/specs/submission-v1.md