Skip to content
Comuvia ForeGlass™
ForeGlass / Developers

Developer reference · 0.1.0 alpha

ForeGlass Python reference

Bounded reads, snapshots, catalogs and normalization.

Public alpha reference. Version 0.1.0 was published on PyPI on 2026-10-01; pin the version. Release status and verification

Version 0.1.0 alpha · published on PyPI 2026-10-01; pin the version.

foreglass is the optional Python reader for supported public ForeGlass artifacts. It retains exact bytes, exposes declared field meanings and normalizes supported records into the neutral comuvia core. It depends only on comuvia>=0.1.0,<0.2.

Import the package before using the examples. Public function and class names below are accessed through foreglass, such as foreglass.PublicClient(). Importing or constructing the client makes no network request. Calls to the explicit fetch methods do.

import foreglass

Python metadata requires 3.11 or later; measured support covers CPython 3.11–3.13 on Windows and Linux x86-64. See evidence concepts for the distinction between an observed artifact, a source assertion, an interpretation and an evaluable forecast.

PublicClient: explicit bounded reads

The constructor accepts keyword arguments only. The following is the complete parameter list; the last three parameters allow clock and sleep injection.

PublicClient(*, origin="https://foreglass.ai",
             allowed_hosts=("foreglass.ai", "www.foreglass.ai"),
             allowed_ports=(443,), transport=None,
             max_bytes=4194304, connect_timeout=10.0, read_timeout=30.0,
             total_timeout=120.0, max_redirects=3, max_retries=2,
             backoff_base=1.0, backoff_max=8.0, retry_after_cap=30.0,
             sleep=time.sleep, clock=time.time, monotonic=time.monotonic)

client.url_for(role: str) -> str
client.fetch(role: str) -> Artifact
client.fetch_bundle(roles: Iterable[str] = ("ledger",)) -> dict[str, Artifact]
client.fetch_archive_file(entry: Mapping, *, proof: bool = False) -> Artifact

url_for constructs a supported URL without fetching it. fetch_bundle explicitly fetches the selected roles. fetch_archive_file reads a selected archive-index entry; it does not crawl arbitrary locations. HttpsTransport(context=None) is the default HTTPS transport and can accept an SSL context.

client = foreglass.PublicClient()  # No request yet.
print(client.url_for("ledger"))   # Still no request.
bundle = client.fetch_bundle(["ledger", "archive_index"])  # Network reads.
print(bundle["ledger"].byte_length, bundle["ledger"].sha256)
ARTIFACTS role Public path
ledger /verification-explorer/ledger.json
ledger_ots /verification-explorer/ledger.json.ots
archive_index /verification-explorer/ledger-archive/index.json
fragility_latest /readings/fragility/latest.json
fragility_countries /readings/fragility/countries.json

Defaults require HTTPS, allow the two declared ForeGlass hosts on port 443, cap each response at 4 MiB and recheck up to three redirects. At most two retries are made for qualifying transient failures, with capped backoff and Retry-After. A response shorter than its declared length is incomplete. Connect/read/total timeouts default to 10/30/120 seconds; an individual socket read can overshoot the total deadline by up to its read timeout. The client sends no cookies, credentials or telemetry.

Reader refusals use comuvia.RefusalError, with named codes such as unknown_artifact, host_not_allowed, insecure_url, response_too_large, incomplete_response, deadline_exceeded and network_error.

SnapshotArchive: retain and accept

SnapshotArchive(path: str | os.PathLike)
SnapshotArchive.create(path: str | os.PathLike) -> SnapshotArchive
archive.intake(bundle: Mapping[str, Artifact], *,
               declared_roles: Iterable[str] = ("ledger",)) -> IntakeResult
archive.accepted() -> Snapshot | None
archive.load(snapshot_id: str) -> Snapshot
replay_bundle(files: Mapping[str, bytes], *,
              fetched_at: str | None = None) -> dict[str, Artifact]

Intake retains the selected bytes and verifies the declared bundle's required structure and identities. Declared roles must be present. The ledger schema and lane counts are checked; when the archive index is declared, it must list the ledger digest. Failed or incomplete intake leaves the previously accepted snapshot unchanged. accepted() returns None when no accepted snapshot exists.

replay_bundle wraps supplied bytes as synthetic artifacts. Even if those bytes came from an earlier public download, replay never establishes a live capture or independently verified publication time. A valid replay returns verified=True, accepted=False, with a snapshot id and the replay_not_accepted issue. Here “verified” means the intake checks passed, not that forecasts are true.

Run this offline example from the extracted example bundle, which supplies the synthetic fixtures. Fixtures are not installed wheel resources. Choose a fresh archive directory.

from pathlib import Path

source = Path("fixtures/synthetic/foreglass-ledger/source")
files = {
    "ledger": (source / "ledger.json").read_bytes(),
    "archive_index": (source / "ledger-archive-index.json").read_bytes(),
}
archive = foreglass.SnapshotArchive.create("foreglass-snapshots")
intake = archive.intake(foreglass.replay_bundle(files), declared_roles=list(files))
print(intake.verified, intake.accepted)  # True False
if not intake.verified or intake.snapshot_id is None:
    raise RuntimeError(intake.issues)
snapshot = archive.load(intake.snapshot_id)

Inspect both issues and findings. Issues such as incomplete_intake, digest_mismatch, schema_mismatch, counts_mismatch and archive_index_mismatch prevent successful intake. Findings such as unrecognized_provider_field report drift without silently discarding the raw value. Invalid archives and retained-data problems can raise RefusalError; ordinary filesystem failures can also occur.

Normalize declared semantics

normalize(snapshot: Snapshot | Mapping[str, Artifact], *, recorded_at: str,
          recorded_by: str = "normalizer:foreglass-python") -> NormalizationResult
encode_identifier(text: str) -> str
capabilities() -> dict

normalize returns records and a report; it does not publish them or append them to a comuvia.RecordStore. recorded_at is supplied explicitly. For the offline snapshot above:

result = foreglass.normalize(snapshot, recorded_at="2026-09-26T00:00:00Z")
print(result.report["mapped"]["questions"])                 # 7
print(result.report["mapped"]["forecasts"])                 # 8
print(result.report["evaluation"]["counts"]["eligible"])   # 0
for item in result.not_mapped:
    print(item.lane_id, item.reason)

The normalizer maps supported binary lanes with a pinned qid into questions and forecasts. Interval, point and blank-qid lanes have named non-mapping reasons. Provider text, caveats and unknown fields are retained; missing target periods, information cutoffs, deadlines, vintage, model version and units remain explicit unknowns where needed.

encode_identifier is the exported identifier-encoding helper. capabilities() describes implemented scope. NormalizationError, a RuntimeError subclass, means the normalizer produced a record refused by the core: treat that as an implementation defect, not a property of the source lane. Ordinary non-mapping reasons belong in not_mapped.

Zero eligible results follow from missing or unsupported metadata, not an assessment of predictive skill. The current documentation's retained public-ledger shape also yields zero eligible forecasts; that is a statement about that shape, not a promise about future live contents. Always inspect the report from the artifacts you actually read.

LaneCatalog and readings

LaneCatalog(ledger: Mapping)
LaneCatalog.from_bytes(data: bytes) -> LaneCatalog
catalog.select(*, kind: str | None = None, metric: str | None = None,
               due_from: str | None = None, due_to: str | None = None,
               status: str | None = None, validation_status: str | None = None,
               caveat_id: str | None = None, arm: str | None = None,
               scope: str | None = None) -> tuple[Lane, ...]
catalog.counts_by_kind() -> dict[str, int]
Lane.from_json(lane: Mapping) -> Lane
read_reading(data: bytes, *, kind: str = "fragility_index_reading") -> Reading
catalog = foreglass.LaneCatalog.from_bytes(files["ledger"])
for lane in catalog.select(kind="binary"):
    print(lane.id, lane.declared_probability, lane.caveat)

Lane preserves its published raw mapping and exposes identifiers, kind, metric, dates, status, unit, value, bounds, source/model text, caveats and withheld fields. Its properties include declared_probability, unknown_fields, coverage_level, full_unit and possibly_truncated. The truncation flag is a labelled length heuristic, not proof that text was cut.

FIELD_MEANINGS documents critical distinctions: lo/hi have no declared coverage level; due is a grading date; sealed_at can have day precision; built is not publication time. Lane.declared_probability returns val for binary lanes and None otherwise. Reading always has is_forecast=False and probability=None; an index reading is not a forecast probability.

Returned types and constants

Export Main fields or meaning
Artifact role, data, request_url, final_url, status, fetched_at, content_type, data_origin, declared_sha256; properties byte_length, sha256.
Snapshot snapshot_id, manifest, artifacts; property data_origin.
IntakeResult verified, accepted, snapshot_id, issues, findings.
Finding code, where, field; a nonfatal provider-drift observation.
NormalizationResult records, not_mapped, findings, report.
NotMapped lane_id, kind, reason, detail.
Reading kind, date, reading, caveats, raw, is_forecast, probability.
ARTIFACTS Supported role-to-path mapping listed above.
FIELD_MEANINGS Read-only descriptions of source-field semantics.
foreglass.__version__ "0.1.0".

CLI and scope limits

foreglass 0.1.0 does not install a separate foreglass command. Use its Python API for fetching, intake and normalization. After exporting records into a core store, use the core's actual CLI:

comuvia --json store show evidence-store --latest
comuvia --json evaluate --store evidence-store
comuvia --json store verify evidence-store

There is no custom forecast execution, hosted API/MCP server, submission/upload method, outcome normalization, interval/point normalization or OpenTimestamps proof verification. .ots proofs are retained as bytes; timestamp assurance remains unavailable. The client does not grant rights to retained source material. Applications decide what they may store or disclose and must preserve declared limitations.