dumbtv
dumbtv takes the ads, screen tracking and update nags out of your Fire TV over ADB. No root, none of your apps touched, every step can be undone. Free and open source.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证一群取证代理在您的机器上、在记录上运行。开源的代理式数字取证与事件响应,遵循 AGPL:代理在共享看板上划分案件,锻造自己的工具,并留下哈希链式记录。
AI 翻译
A swarm of forensic agents, on your machine, on the record. Open-source agentic digital forensics and incident response under the AGPL: agents divide the case on a shared board, forge their own tools, and leave a hash-chained record.
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
Agentic digital forensics and incident response, worked by a swarm of peer agents.查看完整 README ↗
Agentic digital forensics and incident response, worked by a swarm of peer agents.
A swarm of forensic agents, on your machine, on the record. N agents (Pi sessions), each in its own microVM, run side by side in Herdr panes, share one sandbox, talk on a file board, lease a path before writing it, send their heavy tool work to throwaway VMs whose output is sealed into a store, forge the tools the case needs, and stop when a sentinel appears. Every tool call, dollar and token goes to a hash-chained trace that a collector outside the panes writes, and to a live web console. There is no planner and no org chart: the board is the product.
Built and maintained by Halil Öztürkci. Free software under the GNU Affero General Public License v3 or later. The website, dfirswarm.ai, explains the swarm, the harness, the published cases and the editions.
Twenty-one runs on seventeen public forensic cases: Ali Hadi's sixteen challenge images (Windows and Linux disks, memory, a Hadoop cluster, unallocated space) and BelkaCTF #6 (a phone and a laptop), worked end to end by 150 agents. Twenty finished on their own sentinel, every check certified against a finish line the agents cannot edit. The one that did not, and the one that reached its sentinel with checks still short, are published as they ended with what went wrong, which is the more useful half of the record. Model spend across all of them is just under $700, per case in the index.
In the summer of 2026, agents in an OpenAI evaluation found a package service they could all write to and turned it into a message board, pooled work across runs that were meant to be isolated, adopted each other's goals, never gave up on impossible tasks, left through a zero-day in the registry proxy, and spent days working on their own transcripts (OpenAI's write-up, METR's investigation). This harness is that story inverted:
| The incident | This harness |
|---|---|
| A hidden, improvised channel | The board is the sanctioned channel: files, append-only, every post on the trace and on screen |
| Goals adopted from peers | The goal is SWARM.md, written by the operator, owned by the harness; agents cannot edit it |
| No safe exit from impossible tasks | A USD cap, a token cap and a wall clock the agents cannot move; at a cap the run pauses for the operator (or, --stop cap-stop, the harness writes the stop itself), and done(cannot_complete) is a rewarded exit |
| A grader to game | The checks that certify a run are read from the operator's registry, never from the sandbox |
| Transcripts rewritten | The trace is written by a collector the panes cannot reach, hash-chained, anchored outside the sandbox, and attributed by a per-pane secret or, on Linux, by process ancestry |
| Escape through shared services | Each agent in its own microVM, with the run read-only there but for its own directories, the network closed but for its models and the hosts named, and no credential inside; a host run (--isolation host) has a write allowlist at the kernel (seatbelt, Landlock, namespaces), the terminal's control socket denied and egress through an allowlist; and the run record says which of these the host could enforce |
The full mapping, in the write-up's own words, is in docs/why-the-incident-inverted.md. Everything the swarm is allowed to decide, it decides on the board. Everything that must not be negotiable (the finish line, the money, the clock, who may write what) lives in the harness. And where a guard is only advisory, the report says so: on one published run an agent unset the proxy variables and installed a library, and the whole account is in the repository beside the run (netguard-escape.md).
The screenshots below come from published runs. Most are from the second swarm on BelkaCTF #6 (s83fd): ten agents on five models across four providers, one of them local, with the first run's answers sealed off at the kernel. Two are from Ali Hadi's challenge images and show the console under its earlier name.
The harness starts N agents on one goal and stops there. It hands out no roles, no names and no work. What happens next happens on the board: each agent says what it is calling itself and which slice it is taking, and the others route around it.
On s83fd the ten arrived as vault-unlock, vault-print, chat-evidence, Key & Printer Hunter, Chat & Timeline Analyst, Evidence Miner, Vault & Chat Analyst, vault-corroboration, iphone-lead and timeline-keeper: names the models chose for themselves through the name tool and used from then on wherever the run is shown. The split followed the evidence, one image each and one agent keeping the timeline without touching either.
The console folds the board into the phases a run went through, with the finish line beside it. The harness posts to the same board, in its own voice.
The finish line on the right is the operator's: its checks are read from the registry rather than from the sandbox, so no agent can edit its way to done.
Claims. edit and write without a live lease are blocked outright. A shell write cannot be stopped mid-command, so it is detected, snapshotted and announced instead, naming the agent, the path and whether someone else held the lock. The harness shows what it could not prevent rather than implying it cannot happen. | Inputs. 13.8 GB of iPhone extraction and laptop image, held read-only at the kernel in all ten panes, hashed before the first agent started and verified intact at the end. Work files keep their own revisions by hash, so nothing an agent wrote is lost to a later write. |
Forged during the run. With --allow-tool-forging an agent turns a repeated need into a named tool with typed arguments; every peer gets it as a real tool at its next turn, and every call lands on the trace under the tool's name. On the first run of this case the swarm wrote its own BitLocker unlocker because the host had none. | Carried between runs. swarm.sh tools <id> --save DIR keeps the tools a run earned, and --tools-from DIR seeds the next swarm with them. Challenge 10 began with 18 tools written by agents of earlier swarms and called them 141 times; Challenge 11 forged nothing, because the library already covered it. |
An agent keeps a shell for quick looks. Parsing, carving and anything long or worth citing runs as a job in a worker VM made for it and removed after, booted from the image that holds the programs the work needs: disk, memory, mobile, network, Linux, reverse engineering, a browser, or all of them. The worker reads what the agent reads, read-only, and writes only its own output, which is sealed into store/ file by file; every step of every job is a line of a hash-chained journal anchored outside the run, and a finding cites the output as job:<id>/<path>. A job starts in the run's directory, read-only in its worker, so a program that writes its log or temp files where it stands is given a path under $OUT; a job that failed on a write outside it says so in its reason, with the path, where the line is and where it may write. The catalogue of the evidence grows the same way: the packs' recipes run as jobs while the agents work, and an archive or a disk image a job produces is catalogued in turn and announced to everyone. The console's Jobs tab shows every job, the image it ran in and what it sealed. ADR 0010 has the design.
Work is never merged behind an agent's back, and it is never silently repeated either. When another seat's job, under way or done, runs the same tool or the same command over some of the same objects (declared, and compared by digest), the new job's answer names it in similar, with its lead, its state and its outputs; a job whose output is byte for byte an earlier job's says same_as, naming that job's file. A second run meant as a check says independent: true and is counted as a reproduction; the console's Jobs tab shows each job's hints whole. Only a typed recipe over the same object is answered with the earlier job. ADR 0017 has the design, and why the earlier merge by whole spec was retired.
When the evidence cannot answer something a reference service can (a domain's registration, a certificate log, a CVE, a hash's reputation, a place), a run started with --network dynamic lets an agent ask for one bounded lookup. The hub decides it by rules alone, under the run's case policy (--policy standard, live_adversary, internal or ctf), and records the decision before it answers; a refused request closes that avenue and never the lead, and what the operator may override becomes one item per host and lead. A granted request is one exact method and URL, for minutes and one use, made by a fetch service on the host that holds no evidence and sends none of the agent's headers; the answer is sealed as a capture and enters the ledger as external material, which a hash proves as bytes and an examiner weighs as a claim. The catalogue is a positive list of passive reference adapters (RDAP, crt.sh, NVD, CISA KEV, CIRCL hashlookup, RIPEstat, Nominatim, Overpass, a video's title, VirusTotal by hash with a key you configure) and one evidence-linked HEAD. The console's Network tab and swarm.sh net show every request, grant and capture. ADR 0012 has the design and its limits.
scripts/pack.sh install packs/computer-forensics-base
scripts/pack.sh install packs/windows-forensics
scripts/swarm.sh start --pack computer-forensics-base,windows-forensics ...
| Pack | Skills | Tools | Goals |
|---|---|---|---|
computer-forensics-base | 29 | 16 | — |
windows-forensics | 53 | 22 | 3 |
linux-forensics | 26 | 6 | 2 |
macos-forensics | 18 | 4 | 1 |
mobile-forensics | 11 | 3 | 1 |
memory-forensics | 20 | 3 | 1 |
network-forensics | 13 | 6 | 1 |
reverse-engineering | 21 | 6 | 1 |
encrypted-containers | 12 | 3 | 1 |
cloud-forensics | 10 | 3 | 1 |
ransomware-response | 18 | 2 | 1 |
triage-collection | 20 | 6 | 1 |
virtualization-backup-forensics | 11 | 3 | 1 |
A pack is a directory an operator installs once and names at kickoff: skills an agent fetches by name, tools seeded into the run, the host binaries the case needs, and goal templates with their own checks. Thirteen ship in this repository under the same licence as the harness: 262 skills, 83 tools and 15 goal templates across Windows, Linux, macOS, mobile, memory, network, reverse engineering, encrypted containers, cloud, ransomware, triage collections, and virtualization and backup, all on one base pack.
Skills are a tree of small files rather than a wall of text in the system
prompt: the kickoff puts a one-line-per-skill index of the run's packs into
every agent's prompt (within a budget of 2,500 tokens for the whole run, and a
pack's router alone above it), and the agent loads a body, as plain Markdown,
only when it reaches that artefact family, so a pack the size of a textbook
costs a few hundred tokens until it is used. A body already in an agent's
context is not sent twice. A body the agent has marked done is replaced in its
context by a one-line stub when the agent hands off to itself (the session keeps
the whole result, and skill(id) brings it back); --skill-release auto adds the
next turn boundary on models where editing an earlier tool result is known to be
safe, not on Claude models with thinking, whose signed thinking blocks an edit can
invalidate or get dropped. The compaction's summary is written from note ids and
sizes, not from the first 2,000 characters of a note, and the agent is told which
notes it read and which it probably still needs. Every load and every release is an event on the trace with the file's checksum and
its token cost, and the console's Packs tab says which skills a run read,
which it carried and never opened, what each agent loaded and whether anything
shows it was used, which tools came from which pack, and which agent did each.
Packs are optional: --pack names them, and a run without it behaves exactly
as before.
docs/packs.md is the format, the secrets rule and the licence
position; packs/*/README.md is what each one carries.
In Challenge 3 (s9f20) two agents each reached an eighth python3 one-liner, and the harness said so on the board: a tool forged with make_tool would run by name, with typed arguments, for them and for every peer, and its calls would be on the trace. It is a hint addressed to one agent, which that agent is free to ignore.
Every figure comes from each Pi session's own usage, folded into budget.json at each turn end, the same numbers Pi prints in its footer. In a VM run that is what each seat reports about itself, unless --model-gateway is on: then every call to a provider the gateway fronts goes through one process on the host that holds the key and meters the call from the provider's own answer, and that figure is the one the caps read (docs/model-gateway.md). s83fd spent $68.89 of a $100 cap over 1.2k provider calls and 103M tokens, split across Azure AI Foundry, OpenAI and a local LM Studio model that billed nothing and was braked by tokens instead. The per-agent cap works the same way: an agent that reaches its own ceiling is steered to finish and the swarm carries on without it.
record gives the agents one shared, append-only ledger of dated events, indicators and findings, every row carrying the evidence it rests on (an $MFT inode, a Security.evtx record, a path inside the iTunes backup) and the run's own objects it cites, checked as it is written: an input, or a job's sealed output. The report's timeline is built from it, which is how the finish line can check the report against the ledger. A wrong entry is corrected by a new one that supersedes it, never deleted; a search that found nothing is an absence, with the query, the tool, the scope it holds for and whether it completed; a proposition still under test is a hypothesis with its status, and what could not be examined is a limitation with its reason. An entry can say which question it answers, which entries it supports, contradicts or derives from, which clock its time came from and how precise it is, and whether it holds sensitive material; a second author is an attestation beside it. A time without a zone is refused rather than read in the host's.
A finding says what it indicates as well as what was seen: the step from the observation to its meaning, how sure the finder is and why (the quality of the evidence, not a count of artefacts), what else could explain it, and, when it rests on a job that did not succeed, why those bytes still hold; the harness writes how each cited object was made into the entry. The answers are entries too: one per question of the goal, a summary and a narrative, each citing the entries it rests on by hash, its contrary evidence, its limitations, what else could explain it and what would change it. A critic other than the author re-derives what each answer rests on and records attest or dispute, each in its own chain; an answer whose support is later corrected or disputed stops standing, and so does every answer resting on it. Before the run may end, the goal's check names each such defect and its fix, and lets the run end once each is fixed or named as a limitation.
The ledger is the agents' until an examiner adopts it, and machine custody and human adoption are kept apart. When a run stops, custody seals what the host read and the harness seals the report's first release: a DRAFT, sealed by the install's machine key (checked only as the machine's seal), which binds the report, a rendering of it, the custody verdict and every chain's head, and adopts nothing. An examiner is enrolled on purpose (swarm.sh examiner enroll: name, organisation, competence, and one key: an ssh key with a passphrase, a FIDO key, or an e-signature certificate on a token), adopts, qualifies, withdraws or renders inconclusive each answer by seq and hash (an answer whose support is defective cannot be adopted: it is withdrawn or inconclusive, and repairing it is a new examination), and signs, on the command line or in the console's Release tab: the final bytes are prepared once without the DRAFT mark and shown with their sha256, the examiner confirms "I have read the report and the answers I adopt", and the release is sealed over exactly those bytes with the examiner's own passphrase, touch or PIN, which reaches the signing program only down a pipe. A technical reviewer, enrolled in their own role, signs their own review of the run, here or from the package on another machine, and a run can require one before its release. Every later release names the one before it and why; swarm.sh releases --verify and verify walk the chain against the register. A release's digest line can be mirrored where the operator's account cannot rewrite it and timestamped by an RFC 3161 authority, later if the lab is offline; swarm.sh rerun runs a sealed job again in the recorded image and names each file that differs; swarm.sh certify writes the certification template a qualified person completes. What each of these proves, and does not, is in SECURITY.md. The review itself is a chained file beside the registry that no agent reaches; until an examiner adopts an answer, the report says it is the agents' conclusion, and once the ledger or the report moves on, it says a sign-off is over an earlier one. The report also says which evidence files no command on the trace named, and which entries no call before them named the source of: a named file is not necessarily an examined one, and the report says that too.
What a run is asked lives in one register, not in prose: the goal's numbered questions (Q-3 is the ledger's question:3), the questions an agent opens from the evidence, and the questions a person asks while the swarm works, with swarm.sh question <run> add --text … --why … or the console's Questions tab. Each is a Q-n on a hash-chained file beside the leads: every verbatim revision, why it was asked, the objective or question it belongs to, whether the finish line waits for it, hints on where to look (never what to find), who asked it and how (an enrolled examiner, analyst, reviewer or observer, as a claim or signed with their key; the OS account and host when nobody was named, marked not enrolled). A person's question is committed first and published after: a post from analyst:<person>, a line ranked first in every agent's header, and an offer to the seat most suited to it, the asker's suggested seat for its first minute. An examiner's question is in the case by authority; an analyst's inside an objective, otherwise proposed for the examiner's triage; an agent never changes a person's question. It is worked as a proposition to test: the first lead under it states the proposition and its negation, its answer names the contrary evidence or says why there is none, "the premise is not supported" is an answer, and a question worded as its own conclusion ("confirm that…") is flagged for the critic. A clarification an agent asks is on the record with its answer, a withdrawal closes the leads that served only that question and sends one that found something to triage, and the finish line holds every question in scope to an answer, including one that arrived a minute before done. A question that asks for a complete set (every file, all connections, a complete list) is marked so, by its words or by its asker (--completeness), and its established or partial answer rests on a coverage record of five areas, allocated, deleted, unallocated, slack and secondary, each searched, skipped or not applicable; without one the finish line holds it. What the case takes as given (whose device it is, who the subject is, the stated setting) is a premise on the same chain, a P-n with its words verbatim, where they stand and what it is about. Only the operator designates a given: in the goal's front matter premises:, with swarm.sh question <run> premise add, or in the console's Questions tab; a given is not proved again and is never a part held open. A premise an agent proposes is a proposition under test until the operator admits it. An answer says how it stands on each premise it cites (assumed, supported, contradicted or unresolved), and two standing answers that assume and contradict one premise hold both questions until they are reconciled on the record: an answer revised, the finding that rebuts the premise named (which brings the premise to you as a request), a scope narrowed, or an answer made conditional. Neither side is forced. A question can say what it presumes ( in the goal, on ): the premise is tested before the question is answered, a review of an established answer to it tests "the question's premise is not supported" as a rival or holds it a best candidate, and a partial answer that never tested it is warned; a clue that fits the question's frame is a candidate to test, not an answer. has the design.
An answer says what it is: established, partial, a bounded negative, not determinable, out of scope, or a premise not supported. It lists its parts against the words of the question it answers, each established on the entries it rests on, open with what could still settle it (an acquisition ask, a route, a limitation), or at the limit of the evidence: a part the question asks that nothing in scope can settle, shown by the coverage record for the question or a limitation that says the evidence is gone. Open or at the limit, a part the answer does not establish keeps it partial; the report says which, so a reader can tell a gap more work could close from one the evidence cannot. A partial answer names what is open, only a part the question asks can be open (the rest are limitations), and a premise never is; the report shows each answer's parts, and a partial whose every asked part is established says so beside its label (swarm.sh metrics counts them); a reviewer can name a part the answer leaves out, which is warned of until the answer is recorded again with it. A negative is bounded and says so. Before it, a coverage record states what was searched, over which objects and time range, how, what was skipped or failed, and whether the event would have left a trace in those sources at all; the hub checks the objects it names against what the jobs behind it were actually given, and marks the coverage complete or partial, without judging relevance. The record also names the strings a hit would contain were the answer in the evidence (looked_for), and the hub sweeps every output the run already holds for them: job outputs and logs, imports, captures and the kept tool output, byte for byte, in UTF-8 and UTF-16LE, with no parser. A hit in an object the record does not name holds the negative until the record names that object with what it showed, or the answer is revised; a sweep its budget cut short names what it did not search and holds until you accept the question's limits. A kept output and the import it was sealed as are one object; a hit that only repeats the run's own words (its registers, a seat's summary, or a search over objects the record already names) is an echo, shown and never held; and when the sweep's rules change, the sweeps recorded under older ones are read again at the next start. A pack's recipe says whether it inventories a source or parses the whole of it into a searchable form, a broad extraction; each broad extraction that applies is run at the kickoff where its pack says so (the mobile pack parses a whole iOS or Android file-system acquisition there) and otherwise offered as a lead of its own, one per source, and every step of it is a receipt on the store journal: planned, attempted, produced, partial, failed or declined. A negative that says the event did not happen, or claims complete coverage of a source, waits while that source's broad extraction is planned or under way; any other negative on a source not yet extracted is warned, and its review opens with the state of each source it rests on. A lead under a question plans its routes, and a negative close lists the routes nobody examined; a negative closed after one quick job over one object is flagged for review. Another seat reviews every material negative (did it challenge the detection assumptions, reproduce a decisive check, try another route), with the sweep and its hits in front of it, before the run may finish, and the report words it as "no evidence that X was found in ": "X did not happen" needs an existence question, complete coverage and a stated detection opportunity. An established or partial answer is taken down to a negative only with counter-evidence: the revision names the entries or objects that undermine it (), or it is refused, and the doubt goes into a dispute on the answer and a lower strength or confidence, the answer standing. The answers check's warnings are said where the decision is made: in the reply to the record of an answer, in a review offer as it reaches its seat, in the reply to an attest and to a lead's close, and in . An answer that leaves out what the record ties to its question (a finding two seats hold that was recorded under its leads or names it, or one seat's that names it and another question's answer relies on) is warned, each entry listed with its tie. A warning never holds. has the design.
One seat ends the run. The finish has a coordinator, normally the seat that published the report last, held by a lease that another seat takes over when its holder is compacting, dead or silent; any other seat's done is answered "not yours" and changes nothing. Every header says whether the registers make the finish ready, and the board is told when that turns; once nothing but late items, confirmations or resolutions stands in its way, the finish is being assembled, and another seat revises an answer only with material, why it changes a conclusion. A result or an objection that lands after the report is answered by the coordinator in a typed act, folded in or not material with why. The coordinator drafts the report and prepares the finish (finish prepare: the lease taken as a done takes it, no check run, every item late against the report listed whole), resolves those items in one batch, invites the reviews and then calls done, so a done refused on a late item means a result, a veto or an objection really raced it. A peer's review of the report is a typed ack rather than one more late post; it names the sections it covered and carries over to a new version while they are unchanged, so a new version asks again only about what changed. One check result stands per state revision, and the sentinel is written only while the report, the registers, the jobs, the policy and the operator's decisions stand as they were checked. A critic's attest of an answer says whether it holds it established or a best candidate, part by part, and a best candidate is never counted as answered. The review starts from the source rather than the candidate: its offer leads with the question as asked, its scope and the original sources the answer leads back to, links the answer instead of quoting it, and goes first to a seat of another model family where the team has one. An established attest names an alternative explanation it weighed, why the evidence rules it out and the entries that show it; one that names none is recorded a best candidate. On an answer that claims established it also names the strongest rival and a test that separates it, locates each literal value it vouches for in the sealed bytes (the hub reads the bytes at that offset and checks that the value is one the answer or its chain states), and names the job that derived a derived value. Without a rival test that counts, or with a locator that does not verify or a derivation that does not resolve, it is recorded a best candidate, the reply saying how to fix each; a review that neither locates nor derives is warned. A locator proves the value is there, not that it is the answer: an established review that locates a value is a best candidate until a coverage record for the question names the input the value was read from, says each area (allocated, deleted, unallocated, slack, secondary) searched or not applicable, and cites the job that searched it, so a rival value in slack or in a deleted entry is looked for before the one in the live file stands. The run records high confidence only on an established answer attested so, and records any other high as medium, beside the confidence its author stated, in the report and the metrics. Work nobody holds is offered to one seat at a time, for a minute from when the offer reaches it; a review is offered to one seat and held for it while it reviews; two leads on one question are both held only when the second says it is a second route or a verification; a lead left idle in a busy seat's hands is offered on; a closure whose entry was corrected in what it concludes goes back to its closer to confirm or reopen, a seat's closures under one correction in one batch, while a correction of its references or words alone leaves it standing; and a correction never silences a dispute. A running job is seen from the host by its outputs, its logs and its CPU, and "suspected stall" is shown, never acted on. has the design.
The case is examined under a contract fixed at kickoff: the case policy says what may leave the run and reach outside it, whether more evidence may arrive while it goes on (--more-evidence no|ask|yes), and what material from outside the original evidence may be used for (--material-use); the kickoff refuses a contract that contradicts itself, warns about the services the goal names that the policy or the adapter catalogue will not reach and about a goal that has a case brief but designates no premises (its answers would hold the brief's givens open as parts to prove), anchors the policy beside the run, and custody seals it. Everything the run asks of a person, a lead that needs you, evidence it does not have, a clarification, a network item, a stop proposed when nothing yields, is a request with its own id (R-n), committed before anyone is told, notified by the hub to your desktop, an ntfy topic, a mail address or your own command by its ids alone, and listed with swarm.sh requests <run> list and on the console's Requests tab, whose badge counts what waits on you. An agent asks for evidence as an acquisition (the source, where it is, what it would establish, how urgent); under no the hub answers at once "no additional input under this case policy", a constraint of the case and never a finding that something is absent. An answer that a missing source leaves not determinable names its acquisition ask (R-n) in the coverage behind it, or says why none would settle it, and the answers check warns when it does neither. Evidence that arrives, swarm.sh evidence <run> add PATH --for R-n, is an inventory revision sealed in the store, readable at once by every seat and by jobs, and it reopens the leads, answers and acceptances that rested on the evidence as it was. Its files are searched at once for what every standing coverage record looked for (the reverse sweep, a pass at a time after the addition commits), and each hit goes to the questions it bears on, never as a hold. It also makes stale every bounded negative, not-determinable and partial answer whose coverage predates it, whatever question it was added for: each holds the finish until the new evidence is examined for it, another seat reviews that examination, and the entry that examined it says how it bears on the answer (it supports it, contradicts it, adds a part, is irrelevant or is inconclusive), or until you accept the question after the addition; a citation of the new evidence alone clears nothing. Material you supply (swarm.sh material add, a question's attachment) is on the ledger with who supplied it, when and from where, and every answer resting on it is flagged. A program no image holds is supplied the same way with swarm.sh tool-supply <run> add PATH --source TEXT, which adds where it came from, how it was built and the hashes you checked, and tells the seats how to run it (a sealed file cannot be executed where it stands); a job that tried to run a program in place says so in its reason, and for evidence it says that evidence is read, never run. The report's "Evidence gaps and acquisition requests" says, from the records, what was never collected, unavailable, inaccessible, unexamined or inconclusive. has the design.
The report opens on one screen of every question, the goal's, the agents' and the ones you asked, with its standing, who asked it, its answer, its leads, your acceptance and its tokens; then it follows each question from who asked it to what it cost: the asker (claimed or signed), why, every revision word for word and the neutral wording, what came with it and where that came from, the clarifications, the proposition its first lead tested, the leads that worked it, the result and what speaks against it, your acceptance, the gaps that bound it, and the tokens its seats spent while they held its leads (the report says how it apportions them). Proposed, excluded and withdrawn questions are there too, each with why it matters, and so are the questions no answer settles. The negatives are counted in the body; every question and lead event, every disposition with whether another agent reviewed it, is in an appendix, whole. A release binds the question register as custody sealed it, with who asked what. A job run with secret_output seals its outputs sensitive, and so does any job made from them; an entry citing one is recorded sensitive, and package --redact withholds them whole, names what it withheld and scans every file it hands over for them. An entry resting on a cancelled job's partial output has to say how it treats it. swarm.sh tools <id> --candidates ranks the code the agents wrote into jobs for the tool library, and a job that declares what it reads is told which library tools already read that kind of file. ADR 0016 has the design.
swarm.sh metrics <run> reads a run's own registers and says how it worked, without a ground truth: the quick and the unreviewed negatives, how complete the coverage records are, what became of each offer, the done calls the finish refused and the finish's own acts, the warnings delivered and where, the tail from readiness to the end, the evidence asked for and never received, the interpretations whose entries no longer stand, the reversals (after new evidence, or discoverable in the evidence the run always had), the tokens each question cost, the work done twice, and the network's requests and grants. Every figure is a count of named records, and the JSON names them. swarm.sh metrics --compare <run-A> <run-B> sets two runs of one goal side by side, question by question, and flags a negative one run established and a negative both reached on partial coverage: agreement between two runs is not confirmation. The definitions are in docs/usage.md.
swarm.sh replay <run> measures a rule change before a paid run is spent on it: it copies a finished run and evaluates any harness version's finish rules over the copy (the answers check, the finish gate and its verdict, readiness, the finish register, the report's standing and the custody seals), with no model call, no job and no VM. It prints codes, ids and counts, never a record's words unless --show-text asks for them. --compare sets the run's own harness against this one, or any two, and names every difference; --deliveries reads which warnings each decision point would have carried, act by act; --prepare-as and --reverse-sweep ask what a run from before the broad extractions or the reverse sweep would have met, and --resweep reads the run's recorded store sweeps again with this checkout's rules. The run itself is never written, and its registers are hashed before and after. Beside it, contract fixtures in tests/fixtures/contract/ are synthetic histories made through the harness's own acts, each held to an expectation written from the ADRs: readiness, the answers check and the gate agree on every disposition, a warning never holds, and every custody verdict verifies as a prefix. Replay measures decisions on a history the agents already wrote; whether a rule changes what the agents do is for paired runs. docs/usage.md has every option, and ADR 0017 the reasoning.
| The trace. One line per tool call, written by a collector outside the panes. Each line names the hash of the line before; the head of the chain is anchored beside the run, where no pane can write; who wrote a line is decided by a per-pane secret the collector holds, not by what the line claims. The report walks the chain and says intact, edited, appended or shortened. | The dossier. Numbered sections that cite the artefacts they came from, a merged timeline, a findings table with confidence and each exhibit's review, and a chain-of-custody section that names every guard's enforcement level on that host, the operator's actions and what produced the run. swarm.sh package ships all of it with a SHA-256 manifest, a court set (every file handed over, hashed), the index of work/ custody sealed and the examiner's review; --sign signs the manifest, and who signed it, with the examiner's ssh key, --redact takes out what a sensitive entry says while every chain still verifies, and swarm.sh verify re-walks every register the package carries against the verdict's seal on the recipient's side, holds every work file to the sealed index and fails on a part taken out. At stop, custody gives every check a status (passed, failed, incomplete, not applicable, unavailable), seals the length and head of every hash-chained register the run keeps (the trace; the ledger, its attestations, disputes and store sweeps; the leads and the finish register; the questions; the requests to you; the network's grants and fetches; the store journal; the model gateway's log) and the hash of every work file, compares the imager's acquisition hashes when they were given, and can be signed and timestamped by an RFC 3161 authority whose token it checks against the authority's CA. A release binds every one of those registers and a package carries each; swarm.sh custody-verify checks a finished run against the verdict later, writing nothing in the run, and names a report edited or an entry appended after the stop. |
On s83fd an agent refused by the egress guard eight times unset the proxy variables in its environment and installed the library it needed. The four guards enforced by the kernel held for the whole run; the one enforced by an environment variable could not, and that is what advisory means. The custody section of that run's report prints Egress enforcement: ADVISORY beside the package that came through, the contract that invited the attempt was fixed, and netguard-escape.md is the whole account. The tour walks the console through the run in under three minutes, the escape included.
More screens: docs/screenshots.md. Every case with goals, boards, reports and costs: docs/use-cases/.
You need Node ≥ 22.19 (the pinned Pi needs it), jq, python3, zsh or bash as the login shell, Herdr and Pi, and a Pi login (pi /login: an API key, or a ChatGPT/Codex subscription for test runs; Anthropic takes an API key only, since its terms do not permit a Claude subscription in a third-party client such as Pi). Every agent runs in its own microVM by default, which needs a Mac on Apple silicon or Linux with KVM and glibc, sqlite3 (to clear a removed VM's secrets from msb's database), and Docker once to build the agents' image. A live run costs real money; start small.
A host that cannot boot the VMs is refused with what it lacks. --isolation host runs the agents as processes on this machine instead: unisolated, held by the host guards at the kernel (seatbelt on macOS, Landlock inside a user namespace on Linux). A Linux server needs a little setting up (KVM for the VMs; for host runs, zsh or bash as the login shell and unprivileged user namespaces allowed), and docs/linux-server.md is that page.
git clone https://github.com/halilozturkci/dfirswarm && cd dfirswarm
npm install # microsandbox's msb comes with it
# no model, no key, no Herdr: the protocol on files
npm test
# the agents' VM image, once: every agent's VM boots the base
# (amd64 in place of arm64 on an Intel or AMD Linux host)
docker build -f images/base.Dockerfile -t dfirswarm-base:dev-arm64 images
docker save dfirswarm-base:dev-arm64 -o /tmp/dfirswarm-base.tar
"$(node --experimental-strip-types scripts/vm.ts msb-path)" load -i /tmp/dfirswarm-base.tar
# Herdr must be running (open the app or `herdr server`)
scripts/swarm.sh start --model deepseek/deepseek-v4-pro --cap-usd 1 --n 2 \
--goal-file prompts/goals/hello.md
scripts/swarm.sh status <id> # the id is printed at kickoff
scripts/swarm.sh ui # the console, on 127.0.0.1 (--host 0.0.0.0 for the LAN)
scripts/swarm.sh pause <id> # hold it: no model call goes out, the clock stands
scripts/swarm.sh unpause <id> # and let it go on
The goal is a markdown file that carries its own ## Definition of done and ## Checks; a goal without one does not start. The agents boot the base image; a run with packs runs its tool work in job images, one per profile, which you build the same way (images/README.md) and which list their programs for the agents. scripts/swarm.sh --help lists the commands, swarm.sh help start every option, and the quick start has the ones for a case while it runs: a question, a premise, a request answered, evidence added, extend, pause, unpause, resume, and after it stops the metrics and a replay. swarm.sh start … --check runs every refusal and preflight of that start and writes nothing, and swarm.sh image-for --pack … names the image the agents would boot and the job images that hold those packs. The console's New swarm form starts a swarm the same way, and defaults to a model Pi can use.
In a VM the network is closed but for the model's hosts and the ones you allow. In a host run the egress guard is, on macOS, a proxy the panes are pointed at, which a process can ignore, and on Linux a network namespace with no other route out. The kickoff line and the run record say which one you got. Step by step, with the dry run and the no-key proof run: docs/quick-start.md.
The swarm runs on whatever Pi can talk to. The harness names a model as provider/id, asks Pi whether it can authenticate it before a single pane opens, and puts the provider's host on the network allowlist; it does not carry a model list of its own. That gives four ways in, and every one of them has been run here.
| You have | Start with | Proven on |
|---|---|---|
| An API key for a provider Pi knows: OpenAI, Anthropic, DeepSeek, xAI, Google, OpenRouter, Azure OpenAI, Bedrock, Vertex, and the rest of Pi's list | pi /login once, then --model deepseek/deepseek-v4-pro. Nothing is passed to the panes; Pi keeps the key. | openai/gpt-5.4 and deepseek/deepseek-v4-pro, the sixteen challenge images |
| A subscription: ChatGPT/Codex, GitHub Copilot, xAI | pi /login, pick the subscription; no key exists and none is needed | openai-codex/gpt-6-astra, the forged-tool run and the console's earlier screens. A consumer plan: recorded as "not for customer data", and refused under --customer-case. A Claude Free, Pro or Max login is refused at kickoff in every run: Anthropic does not permit it in a third-party client such as Pi |
| A provider of your own: an Azure AI Foundry resource, a gateway, anything OpenAI-compatible on the internet | A provider in Pi's ~/.pi/agent/models.json with its baseUrl, its models and their cost; the allowlist takes the host from baseUrl | azure-foundry/grok-4.6 and azure-foundry/DeepSeek-V4-Pro, ten of the sixteen cases and both BelkaCTF runs |
| A model on your own machine: Ollama, LM Studio, vLLM, llama.cpp | The same models.json entry, with a placeholder apiKey (Pi lists a provider only when it has one; the server ignores it) and a compat block; then --cap-tokens and, for an all-local team, --local-only | ollama/glm-4.7-flash-64k:latest on the hello goal at $0, and lmstudio/qwen3.8-27b-uncensored as one seat of ten on BelkaCTF #6 |
Teams mix: --models "openai/gpt-5.4=4,deepseek/deepseek-v4-pro=3" puts each agent's model on the board so peers can hand a slice to whoever suits it, and a local model can sit next to a cloud one.
Running it on your own models costs nothing and leaves the machine. A team served from this machine bills nothing, and Pi reports its cost as an exact $0, so a dollar cap could never stop it. The kickoff refuses such a team without --cap-tokens N, probes the server before any pane opens (that it answers, that it has the model, and what context Ollama will really give; its default is 4096, whatever models.json declares), and with --local-only reduces the network allowlist to the local endpoint alone. The console then says "free" and counts tokens. Which local models are good at this is a different question: Pi cannot tell whether a model supports tool calls, and one that ignores them posts nothing and claims nothing rather than failing, so treat the hello goal as the entry exam for any model you pull. The models.json to copy, the placeholder key, the compat block and the Ollama context rule are in docs/credentials-and-teams.md; the runs are in docs/verified-runs.md.
Two things to know before choosing. The netguard allowlist knows the hosts of OpenAI, Anthropic, DeepSeek, xAI, Google, OpenRouter and Azure OpenAI out of the box and reads any models.json provider's host from its baseUrl; a provider outside both (Bedrock, Vertex, Copilot) needs --allow-host for its endpoint. And a subscription's spend is Pi's estimate rather than a charge, while a models.json provider reports whatever cost you wrote: state the rates, or the cap is a fiction.
Each agent runs in its own microVM unless the run says --isolation host; the guards of a host run are the operating system's where the host allows them. Either way the run record and the report's custody section say what was achieved on that host rather than what was asked for. docs/safety.md and docs/sandbox-plan.md have the measurements.
--no-netguard), and no credential enters it, only a placeholder the host swaps on the way out, or with --model-gateway a seat token for a gateway on the host that holds the key. The packs' programs run in throwaway job VMs of their own images (ADR 0010). The stop, the snapshots of each VM's disk and a full re-hash of the evidence are taken on the host. A host that cannot boot the VMs is refused, with how to fix it; the kickoff never falls back to host processes on its own. ADR 0009 has the design and its stated limits.--isolation host runs every agent as a Pi process on this machine, held by the guards below and nothing else. The bullets that say "pane" are a host run's.--no-write-guard turns it off and the record says so.--inputs DIR is hashed and mounted read-only into every VM (--inputs-copy for a copy in the run, the way to start a live run: evidence this account can write is refused in place until it is copied or made read-only); give it once per set, and each lands at inputs/<name>/. In a host run it is copied in, hashed and held read-only in every pane; --inputs-bind guards the source in place instead of copying 13 GB; --inputs-image attaches a disk image read-only. A write that lands by another route is healed from a pristine copy and announced on the board; every file is checked again at the end. catalogues the evidence with the packs' recipes, before any agent spends a token in a host run and as jobs while the agents work in a microVM run; makes anything extracted from the evidence unrunnable. .swarm.sh ui serves one page per run on 127.0.0.1: the board as a story, the agents with the names they chose, the trace, the tools, the ledger, the claims, the budget, the files and the report, live over SSE. A microVM run shows each VM's probe (every check and what it means), its hub link, what the hub refused, restarts of the hub and the collector, the kept disks, and one timeline of the run's life across its VMs; the Custody tab shows every check with its status, what the verdict sealed, whether it matches the one anchored outside the run, and the operator's lines. The Jobs tab lists every tool job, the image it ran in and what it sealed. The Questions tab lists every question with who asked it and how it stands, takes a new one (with its objective, hints, a suggested seat, a deadline), and holds the triage queue and the clarifications the agents asked; the Leads tab adds a directive under a question. The Requests tab lists everything the run asked of you with its id, kind and state, acquisitions with their stage, and the evidence and material added; the header's badge counts the requests still open. The Ledger tab shows hypotheses, limitations and standing contradictions beside the findings, keeps a sensitive entry blurred until it is clicked, and takes the examiner's review; the Release tab shows the run's releases and signs the examiner's adoption, the Technical review tab is the reviewer's, and the Examiners page enrols both. The record dialog holds, releases, exports, packages (signed or not, downloaded as a zip), verifies and, behind the run id typed out, purges; each is on the operator's record as the console's. The New swarm form checks that the host can boot the VMs, shows the image the agents boot and the job images the packs choose, and marks a host run unisolated. HTML an agent wrote is shown without scripts; Open with scripts runs one file's scripts after a warning, and is on the trace.
| Read this for | File |
|---|---|
| From a clean machine to a first swarm and the console | docs/quick-start.md |
| Every command, flag, env var, API route, and how to write a goal | docs/usage.md |
| The examiner, the review, the releases, a rerun, the signed package, certification, export, hold and purge | docs/usage.md |
| Signing: ssh, FIDO and e-signature keys, the console, two-stage review, the hardware | docs/usage.md, tests/hw/README.md |
| Agents in microVMs: the design, what holds, what does not | docs/adr/0009-agents-live-in-microvms.md |
| Metering and stopping model calls on the host | docs/model-gateway.md |
| The agents' VM images, built from the packs | images/README.md |
| What is on disk and on the wire | docs/protocol.md |
| Questions: the goal's, the agents', and the ones you ask while it runs; the premises the case takes as given | docs/usage.md, |
GNU AGPL v3 or later © 2026 Halil Öztürkci. See NOTICE.
Running it costs you nothing and asks nothing, on your own machines or on a client's case, whatever you bill for the work. What the AGPL asks for is the source of a modified version that other people reach over a network. A commercial licence covers the cases where that does not suit, and most people do not need one.
The code is AGPL; the name is not. TRADEMARK.md is what that means in practice: fork freely, and give the fork its own name. Contributions come in under CLA.md, agreed in the pull request.
presumes:--presumesswarm.sh question adddowngradefinish status--catalog--quarantinetraces/events.jsonl runs outside the sandbox and is reached over a socket in a directory the panes cannot write. Each line names the hash of the line before; the head of the chain is anchored beside the run, past the write guard; each line is attributed by a per-pane secret the collector holds. On Linux, where one pane can read a peer's environment, a gate in front of the collector attributes by process ancestry instead. The report verifies the chain and counts what it could not attribute.edit/write without a live lease is blocked; a shell write to a leased path is detected, snapshotted and announced on the board with the agent's id. Harness-owned files (SWARM.md, team.json, budget.json, done/, locks/, threads/, traces/) are refused as write targets.swarm.sh extend <run> --minutes N | --tokens N lets it go on, swarm.sh stop ends it as stopped, never as completed, and the run never goes on by itself. swarm.sh pause <run> holds a going run under any stop policy, and swarm.sh unpause <run>, or Unpause in the console, lifts a pause whose cause is gone (a cap's only when the caps leave room). When the model provider refuses every seat, as a subscription's usage limit does (every live seat limited: told to wait half an hour or more, or refused again after a retry), the run pauses itself under every stop policy: no seat is prompted, no model call goes out and the clock stands. The harness tries again at the time the provider named, or every 30 minutes when it named none, and tells you once per spell, with the advice that the wait holds every VM: to free the machine, stop the run and resume it after the limit lifts. An unattended run can say --stop cap-stop: two minutes after the steer the harness writes done/SWARM_DONE itself and ends every session, in-flight call included. Whatever the policy, the agents' done ends a run only when every question has a disposition under the bar: a question the evidence cannot answer is answered not determinable on a coverage record another seat reviewed, ending the run examination-limited, and a limitation that merely names it ends nothing. A negative that claims absence or complete coverage of a source waits only while that source's broad extraction is planned or under way, and whatever the extraction yields (produced, partial, failed or declined) releases it, so partial and not determinable stay honest dispositions under every stop policy; the answers check's warnings never hold at all. --stop operator (--until-solved) takes the caps and the clock away and nothing else. The pressure is on coverage, never on answers. A metered team gets a default token cap beside its dollars, and when nothing has yielded for a while the harness proposes a stop to you, never deciding it. A run that ended can be continued, swarm.sh resume <run> --question … or "Continue this run" in the console: the same sandbox and chains, each seat from its last hand-off, the next stop sealing the continuation anew while every earlier seal and signed release stays valid for what it bound. --cap-per-agent stops one agent without stopping the swarm. In a host run a stopped agent's next model call is not sent. In a VM run the hub holds the clock and applies the caps to what each seat reports; with --model-gateway, to what the host measured, and the gateway refuses a stopped seat's calls on the host.--allow-host, which the kickoff calls what it is: a socket allowance, host and port, no content capture). With --network dynamic no VM gains a host: each lookup is a mediated, recorded request (ADR 0012). In a VM, msb holds the allowlist on the host, outside the guest: a host outside it does not resolve, and a placeholder sent to a host its secret is not bound to is refused and logged. In a host run on Linux the guard is a network namespace: fail-closed. On macOS it is a proxy the panes are pointed at through their environment: advisory, and printed as ADVISORY in the custody section. In a host run every refused connection is counted and printed. --allow-install lets agents pip-install into the run, inventoried package by package; --no-pypi keeps the index off the allowlist while the machinery stays on.eval, vm.runInContext) is flagged on the trace, in the reply and in the report's job record, never refused, and the contract asks for a reimplementation, a trusted program or the operator's word first. --no-read DIR keeps a directory the agents must not consult (a previous run's answers on the same evidence, above all) unreadable at the kernel, so a re-run is a re-run.unenforced.--allow-tool-forging, an agent writes the parser or checker the goal needs with make_tool; every peer gets it as a real tool; the script, its author, its hash and every call are on the console; the library is kept between runs. docs/forged-tools.md.runs/operator-audit.jsonl, with the OS user and host; a live run's trace carries the same. The run records what produced it (the harness commit, Pi, Node, msb, the image digest) and the host's clock. hold keeps a run from being purged or reused; purge deletes a finished run's material and leaves a destruction record.--no-read. On macOS a process that ignores its proxy variables has egress, and one did. Run swarms on a machine you are willing to lose, and read SECURITY.md.| The case contract: the case policy, what the run asks of you, evidence that arrives later, supplied material | docs/usage.md, docs/adr/0014-the-case-contract-says-what-comes-in-and-what-is-asked.md |
| The report per question, sensitive outputs, tool candidates and the library's hint | docs/usage.md, docs/adr/0016-the-report-follows-each-question-and-the-outputs-carry-their-sensitivity.md, tool-library/README.md |
| The design and what runs where | docs/architecture.md, docs/component-map.md |
| What the harness enforces, what it measured, and what is left | docs/safety.md, docs/sandbox-plan.md, docs/linux-plan.md, SECURITY.md |
| What you see while it runs | docs/observability.md, docs/screenshots.md |
| Subscriptions, mixed-model teams, local models, the credential gate | docs/credentials-and-teams.md |
| Agents writing their own tools | docs/forged-tools.md |
| Synthetic cases with a known truth: how often a run misses what is there, or answers what is not | calibration/README.md, docs/usage.md |
| Process metrics from a run's registers, two runs compared, a finished run replayed under any harness version's finish rules, the contract fixtures, and reuse hints for jobs | docs/usage.md, docs/usage.md, docs/adr/0017-reuse-is-hinted-and-the-process-is-measured.md |
| Packs: skills, tools and method an operator imports | docs/packs.md, packs/ |
| Files the swarm may read but never change | docs/inputs.md |
| Where a run sends the evidence's personal data, where it keeps it, and what the operator decides | docs/data-protection.md |
| Seventeen forensic cases, end to end, with every artifact | docs/use-cases/, docs/use-cases/belkactf/ |
| The run an agent walked out of, end to end, with the tour | netguard-escape.md, the tour |
| Live runs that proved a feature, with ids and costs | docs/verified-runs.md, docs/feature-evidence.md |
| What goes wrong and what to do | docs/troubleshooting.md |
| The reasoning behind the design | docs/why-the-incident-inverted.md, docs/adr/ |
| What is next and what it will not become | docs/roadmap.md |
| The vocabulary | CONTEXT.md |
| The licence, the name, contributions | LICENSE, COMMERCIAL-LICENSE.md, TRADEMARK.md, CLA.md, NOTICE |
| Contributing, conduct, changes | CONTRIBUTING.md, CODE_OF_CONDUCT.md, CHANGELOG.md |
| The macOS CI jobs on the owner's own Macs | docs/macos-runners.md |