Skip to content

edge.bridge

edge.bridge

Zone B — research-airlock bridge (BUILD-SPEC §16; Invariant 2 & 11).

The only component that touches the airlock S3 bucket. It carries de-identified criteria out and public literature in, as opaque JSON, over the filesystem handoff the sealed core writes. It has no vault handle and the core never imports it — network and private data never share a component.

ResearchBridge dataclass

handoff instance-attribute

s3 instance-attribute

bucket instance-attribute

requests_prefix = 'requests/' class-attribute instance-attribute

results_prefix = 'results/' class-attribute instance-attribute

push_requests()

PUT every pending criteria request to S3 requests/. Returns pushed ids.

pull_results()

GET any finished literature results for pending ids; write them to the handoff.

sync_once()

One push + pull cycle. Returns (#pushed, #pulled).

run(*, poll_interval_s=5.0, max_cycles=None)

S3Client

Bases: Protocol

The minimal S3 surface the bridge needs — satisfied by a boto3 client or a fake.

Deliberately tiny: no delete, no bucket admin. The bridge can PUT requests and GET results and nothing else; cleanup is handled by the bucket lifecycle, not the bridge, so the bridge's IAM stays least-privilege (Invariant: tight IAM).

put_object(*, Bucket, Key, Body)

get_object(*, Bucket, Key)

list_objects_v2(*, Bucket, Prefix)

build_bridge(config=None)

Wire a real bridge from config. boto3 is imported LAZILY here so importing this module (e.g. in tests with a fake S3 client) never requires boto3 — and so boto3 never appears in the sealed core's dependency surface.

bridge

The research-airlock bridge (Zone B) — the ONLY component that touches S3 (§16).

It carries de-identified criteria OUT (handoff → S3 requests/) and public literature back IN (S3 results/ → handoff), and does nothing else. Critically:

  • It has no vault handle. It is constructed with a handoff directory and an S3 client — not the vault path, not any store. The private corpus is structurally unreachable here (Invariant 2): network and private data never share a component.
  • It is a dumb pipe. It never parses the criteria or the papers; it moves opaque JSON bytes. So even a bug here cannot leak corpus content — there is no corpus to leak, and the outbound bytes are whatever the sealed core already de-identified.
  • core never imports this module (the static import-firewall enforces it). The core reaches the bridge only through files on disk.
Lifecycle of one request
  1. core writes handoff/requests/<id>.json (de-identified criteria).
  2. bridge PUTs it to s3://bucket/requests/<id>.json, records it pending (handoff/sent/).
  3. cloud Lambda fetches public literature, writes s3://bucket/results/<id>.json.
  4. bridge GETs results for pending ids, writes handoff/results/<id>.json, clears pending.
  5. core reads handoff/results/<id>.json and ranks inside the walls.

REQUESTS = 'requests' module-attribute

RESULTS = 'results' module-attribute

SENT = 'sent' module-attribute

ResearchBridge dataclass

handoff instance-attribute
s3 instance-attribute
bucket instance-attribute
requests_prefix = 'requests/' class-attribute instance-attribute
results_prefix = 'results/' class-attribute instance-attribute
push_requests()

PUT every pending criteria request to S3 requests/. Returns pushed ids.

pull_results()

GET any finished literature results for pending ids; write them to the handoff.

sync_once()

One push + pull cycle. Returns (#pushed, #pulled).

run(*, poll_interval_s=5.0, max_cycles=None)

build_bridge(config=None)

Wire a real bridge from config. boto3 is imported LAZILY here so importing this module (e.g. in tests with a fake S3 client) never requires boto3 — and so boto3 never appears in the sealed core's dependency surface.

protocol

Airlock wire layout (BUILD-SPEC §16). Mirrored, not imported, by the sealed core.

The bridge is a dumb pipe: it shuttles opaque JSON between the filesystem handoff and S3 and never interprets the payloads (it can read neither the vault nor the de-identified criteria's meaning — it just moves bytes). The only thing it needs to agree on with the core and the cloud fetcher is the key layout, kept here so an edit can't silently drift it:

handoff/requests/<id>.json   <->   s3://<bucket>/requests/<id>.json   (outbound)
handoff/results/<id>.json    <->   s3://<bucket>/results/<id>.json    (inbound)

Outbound is the firewall direction: the bridge only ever PUTs whatever the core placed in requests/, which by construction is de-identified criteria (the corpus cannot reach here — this component has no vault handle and core never imports it; Invariant 2 & 11).

S3Client

Bases: Protocol

The minimal S3 surface the bridge needs — satisfied by a boto3 client or a fake.

Deliberately tiny: no delete, no bucket admin. The bridge can PUT requests and GET results and nothing else; cleanup is handled by the bucket lifecycle, not the bridge, so the bridge's IAM stays least-privilege (Invariant: tight IAM).

put_object(*, Bucket, Key, Body)
get_object(*, Bucket, Key)
list_objects_v2(*, Bucket, Prefix)

request_key(prefix, request_id)

result_id_from_key(prefix, key)

results/abc.json -> abc; None if the key isn't a result object.