Skip to main content
\\(oo)//  bridgeline
//    \\

A crawling agent harness in which page content structurally cannot become an instruction — and a claim you can falsify on your own machine in five seconds:

uvx bridgeline verify

That runs sixteen prompt-injection attacks through an undefended agent and through bridgeline, offline, with no API key. The undefended agent is hijacked by 14 of them. bridgeline leaks on 0. The exit code is the verdict.


An agent that reads the open web is reading attacker-controlled text. bridgeline's answer is not to detect that, but to make it not matter: a trusted planner commits the complete plan before any byte is fetched, and nothing read afterwards can add, remove, reorder or redirect a tool call.

a bridgeline run: the plan committed up front, then a page's attempt to redirect the crawl refused

That image is a real run against demo/serve.py, exported from the terminal rather than mocked. The fixture page carries an injected instruction to fetch attacker.test. The plan named its destinations before the page was read, so the link is refused — and the refusal is the only thing on screen that glows.

A bridge line is the first silk thread an orb-weaver casts across a gap, before any web exists. Everything else is anchored to it, and nothing can exceed it.

Use it

uv tool install 'bridgeline[anthropic]'      # or [gemini], or both; pip works too

bridgeline "Summarize the release notes at https://example.com/docs/notes and write the summary to notes/summary.md"

You get the committed plan first, and approve it, edit it in $EDITOR, or quit. Every destination in it — the seed URL, every field of scope, every write_file path — is a literal; every {slot} sits in a content position. That is the whole invariant, on one screen, before anything is fetched. --dry-run prints the plan as JSON and exits; --yes skips the gate.

Chrome goes to stderr and the answer to stdout, so bridgeline … > out.md gives you a clean file, and a redirected stream gets no colour and no mascot.

With no API key it still runs, on deterministic stubs: the crawl is real and the plan is sensible, but the "answer" is an echo of page text rather than a summary. That configuration exists for the security measurement, not for use.

Two layers

Mechanism Question
Layer 1 plan-then-execute + capability gate Is this call plan-derived at all?
Layer 2 bouncer-core's ContractEngine, unmodified Is this argument value permissible?

Layer 1 is the proof — the attack suite passes with Layer 2 switched off (--no-guard, and the layer-1-only column of bridgeline verify). Layer 2 is an independent contract witness over the frontier URLs Layer 1 structurally cannot see.

A crawl cannot enumerate its URLs at plan time, which is what usually makes plan-then-execute useless for one. bridgeline's plan commits a destination space instead of a list: a discovered link is followed only if a pure function of the URL string and plan-fixed literals admits it. Untrusted pages choose which member of that space is visited. They can never choose a non-member.

Both layers are mine

That weakens the independence claim, so here is what to check instead of taking it on trust:

  • Layer 2 is never load-bearing. The full acceptance suite passes with the contract engine replaced by a NullGuard (SPEC criterion S4). Whoever wrote Layer 2, the security result does not rest on it.
  • No private access, enforced rather than asserted. A test walks the AST of every module in src/ and fails on any reach into another object's private attributes (S5).
  • The integration notes are complaints. FINDINGS.md records four things the engine made awkward — an undocumented by-reference retention, a permissive fallback that had to be refused, an unsynchronized engine, and a dependency split it needed. That is what a constrained consumer writes, not an author shaping both sides to fit.

What the claim was measured in

The security result is measured only with no API key: a deterministic planner and a deliberately hijacked extractor that does whatever a page says. bridgeline verify is that configuration. Classifiers, LLM judges and instruction-hierarchy prompting are not used anywhere — not as a fallback, not as defense in depth.

Running with a key is a configuration the security claim was not measured in. Structurally it should be safer — a live extractor is less hostile than one built to be hijacked. The genuinely new surface is the planner: with a key, it is an LLM, and it is the thing that commits the scope. The plan gate is where a scope broader than you meant gets caught. --yes skips it.

A live planner does target the plan schema: 13 of 15 blind-authored benign tasks produced a valid plan against Gemini 2.5 Flash. Extraction quality with a live model is untested.

What it does not claim

  • Confidentiality of destinations, not integrity of routing. A malicious in-scope page decides which in-scope pages get visited and in what order, and can spend the page budget. It cannot send content anywhere the plan did not fix.
  • An extracted URL is never fetched under the default policy, even a legitimate one. That is the price, not a bug.
  • DNS rebinding between resolution and connection is a documented, open window.
  • Sixteen attacks by one blind author is not AgentDojo.

The full accounting is in FINDINGS.md.

Reproduce everything

git clone https://github.com/Ezed9/bridgeline && cd bridgeline
uv sync --extra dev
uv run bridgeline verify              # the attack suite
uv run python -m demo.utility_demo    # 15 benign tasks, defended vs undefended
uv run pytest -q
  • SPEC.md — the invariant and the success and kill criteria, committed before any implementation existed, and corrected once in the open
  • FINDINGS.md — results, the narrow claim, and what it costs
  • docs/mascot-variants.md — how the spider was chosen

bridgeline was called crawlgate until 2026-09-12. SPEC.md still uses that name throughout, deliberately: it is a pre-registration. It has been amended once, on 2026-09-06, to narrow an invariant that overclaimed — git log -p SPEC.md shows exactly what changed, and the commit message says why.

Python 3.12+. MIT licensed.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bridgeline-0.1.0.tar.gz (179.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bridgeline-0.1.0-py3-none-any.whl (55.6 kB view details)

Uploaded Python 3

File details

Details for the file bridgeline-0.1.0.tar.gz.

File metadata

  • Download URL: bridgeline-0.1.0.tar.gz
  • Upload date:
  • Size: 179.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for bridgeline-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9bdfb79c1dbfc124ae76c17f54012ef5bc719b22145c3f9063c7ec27495d714d
MD5 88e08119b55e23abc3bf4cfd688a3b6b
BLAKE2b-256 72a319514a818e7881452bd1c7d97d91c271615365762f72ffe614f7568e88e6

See more details on using hashes here.

Provenance

The following attestation bundles were made for bridgeline-0.1.0.tar.gz:

Publisher: release.yml on Ezed9/bridgeline

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file bridgeline-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: bridgeline-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 55.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for bridgeline-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 914b734529030a100d49170261e445e9f97640765d4e88a90b26405ed3a6485f
MD5 bafc252b3ca8f09bcca5d1936d58a7e8
BLAKE2b-256 4cc8db2bb986d8c6e652b30b5fe9f61190569cc0cf33bd8f08b9ebec3a043f9c

See more details on using hashes here.

Provenance

The following attestation bundles were made for bridgeline-0.1.0-py3-none-any.whl:

Publisher: release.yml on Ezed9/bridgeline

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page