mostlyright-data 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mostlyright/data_harness/__init__.py +158 -0
- mostlyright/data_harness/acquisition/__init__.py +55 -0
- mostlyright/data_harness/acquisition/http.py +2773 -0
- mostlyright/data_harness/acquisition/parsing.py +809 -0
- mostlyright/data_harness/acquisition/ranges.py +495 -0
- mostlyright/data_harness/acquisition/result_download.py +360 -0
- mostlyright/data_harness/acquisition/retention_admission.py +248 -0
- mostlyright/data_harness/acquisition/sandbox.py +4888 -0
- mostlyright/data_harness/acquisition/url_policy.py +530 -0
- mostlyright/data_harness/agent_runtime.py +2743 -0
- mostlyright/data_harness/assets/logo-ink.svg +31 -0
- mostlyright/data_harness/backends/__init__.py +28 -0
- mostlyright/data_harness/backends/pandas_backend.py +350 -0
- mostlyright/data_harness/backends/polars_backend.py +366 -0
- mostlyright/data_harness/backends/protocol.py +124 -0
- mostlyright/data_harness/backends/reference.py +83 -0
- mostlyright/data_harness/backends/registry.py +55 -0
- mostlyright/data_harness/backends/restrictions.py +126 -0
- mostlyright/data_harness/canonical.py +333 -0
- mostlyright/data_harness/catalog_job.py +625 -0
- mostlyright/data_harness/cli.py +5398 -0
- mostlyright/data_harness/contracts.py +53 -0
- mostlyright/data_harness/coordinator.py +1307 -0
- mostlyright/data_harness/deploy.py +924 -0
- mostlyright/data_harness/deploy_target.py +312 -0
- mostlyright/data_harness/deployment_evidence.py +1067 -0
- mostlyright/data_harness/event_presentation.py +576 -0
- mostlyright/data_harness/events.py +2152 -0
- mostlyright/data_harness/fast_delimited.py +239 -0
- mostlyright/data_harness/fleet.py +237 -0
- mostlyright/data_harness/formats.py +236 -0
- mostlyright/data_harness/governors.py +1163 -0
- mostlyright/data_harness/hosted_bootstrap.py +972 -0
- mostlyright/data_harness/hosted_crawler.py +1115 -0
- mostlyright/data_harness/hosted_crawler_container_smoke.py +351 -0
- mostlyright/data_harness/hosted_crawler_fetch.py +423 -0
- mostlyright/data_harness/hosted_crawler_job.py +1277 -0
- mostlyright/data_harness/hosted_crawler_protocol.py +676 -0
- mostlyright/data_harness/hosted_dataset.py +1500 -0
- mostlyright/data_harness/hosted_deploy.py +3037 -0
- mostlyright/data_harness/hosted_handoff.py +62 -0
- mostlyright/data_harness/hosted_ingestion_contract.py +504 -0
- mostlyright/data_harness/hosted_ingestion_job.py +356 -0
- mostlyright/data_harness/hosted_ingestion_job_smoke.py +40 -0
- mostlyright/data_harness/hosted_session_container_smoke.py +194 -0
- mostlyright/data_harness/hosted_session_worker.py +3554 -0
- mostlyright/data_harness/hosted_session_worker_job_smoke.py +46 -0
- mostlyright/data_harness/hosted_worker.py +6784 -0
- mostlyright/data_harness/ingestion/__init__.py +56 -0
- mostlyright/data_harness/ingestion/contracts.py +461 -0
- mostlyright/data_harness/ingestion/faults.py +42 -0
- mostlyright/data_harness/ingestion/gcs_store.py +1162 -0
- mostlyright/data_harness/ingestion/spool.py +130 -0
- mostlyright/data_harness/ingestion/store.py +885 -0
- mostlyright/data_harness/key_seam.py +434 -0
- mostlyright/data_harness/linux_process_boundary.py +262 -0
- mostlyright/data_harness/local_contracts.py +2880 -0
- mostlyright/data_harness/local_search/__init__.py +5 -0
- mostlyright/data_harness/local_search/build_index.py +1087 -0
- mostlyright/data_harness/local_search/contracts.py +920 -0
- mostlyright/data_harness/local_search/query_trace.py +266 -0
- mostlyright/data_harness/local_search/retrieval.py +700 -0
- mostlyright/data_harness/local_search/sealed.py +474 -0
- mostlyright/data_harness/local_search/service.py +784 -0
- mostlyright/data_harness/nbrender/CONTRACT.md +212 -0
- mostlyright/data_harness/nbrender/__init__.py +12 -0
- mostlyright/data_harness/nbrender/chrome.py +359 -0
- mostlyright/data_harness/nbrender/code_body.py +266 -0
- mostlyright/data_harness/nbrender/document.py +407 -0
- mostlyright/data_harness/nbrender/frame.py +275 -0
- mostlyright/data_harness/nbrender/interactive.py +337 -0
- mostlyright/data_harness/nbrender/markdown_body.py +477 -0
- mostlyright/data_harness/nbrender/mr_components.py +134 -0
- mostlyright/data_harness/nbrender/outputs_data.py +595 -0
- mostlyright/data_harness/nbrender/outputs_rich.py +906 -0
- mostlyright/data_harness/nbrender/outputs_source.py +260 -0
- mostlyright/data_harness/nbrender/outputs_stage.py +176 -0
- mostlyright/data_harness/nbrender/outputs_text.py +400 -0
- mostlyright/data_harness/nbrender/parse.py +394 -0
- mostlyright/data_harness/nbrender/status.py +40 -0
- mostlyright/data_harness/nbrender/tokens.py +1295 -0
- mostlyright/data_harness/notebook.py +1710 -0
- mostlyright/data_harness/offline.py +2049 -0
- mostlyright/data_harness/operation_registry.py +1007 -0
- mostlyright/data_harness/operator_setup.py +239 -0
- mostlyright/data_harness/pipeline.py +6428 -0
- mostlyright/data_harness/plan_graph.py +2026 -0
- mostlyright/data_harness/preparation/__init__.py +104 -0
- mostlyright/data_harness/preparation/contracts.py +1017 -0
- mostlyright/data_harness/preparation/engine.py +221 -0
- mostlyright/data_harness/preparation/errors.py +14 -0
- mostlyright/data_harness/preparation/gates.py +751 -0
- mostlyright/data_harness/preparation/joins.py +574 -0
- mostlyright/data_harness/preparation/profile.py +384 -0
- mostlyright/data_harness/preparation/table.py +217 -0
- mostlyright/data_harness/preparation/transforms.py +568 -0
- mostlyright/data_harness/progress_events.py +534 -0
- mostlyright/data_harness/readers/__init__.py +46 -0
- mostlyright/data_harness/readers/containers.py +963 -0
- mostlyright/data_harness/readers/contracts.py +542 -0
- mostlyright/data_harness/readers/delimited.py +257 -0
- mostlyright/data_harness/readers/grib2/__init__.py +33 -0
- mostlyright/data_harness/readers/grib2/admission.py +722 -0
- mostlyright/data_harness/readers/grib2/decode.py +1009 -0
- mostlyright/data_harness/readers/grib2/geometry.py +1133 -0
- mostlyright/data_harness/readers/grib2/portable_math.py +501 -0
- mostlyright/data_harness/readers/json_tabular.py +485 -0
- mostlyright/data_harness/readers/registry.py +514 -0
- mostlyright/data_harness/readers/samples/README.md +110 -0
- mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/cities.csv.gz +0 -0
- mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/expected.json +24 -0
- mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/cities.csv.gz +0 -0
- mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/expected.json +24 -0
- mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/cities.tar +0 -0
- mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/expected.json +24 -0
- mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/cities.tar +0 -0
- mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/expected.json +24 -0
- mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/cities.zip +0 -0
- mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/expected.json +25 -0
- mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
- mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/expected.json +25 -0
- mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
- mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/expected.json +25 -0
- mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/cities.csv +3 -0
- mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/expected.json +23 -0
- mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/cities.tsv +5 -0
- mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/expected.json +25 -0
- mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/expected.json +30 -0
- mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/observations.csv +5 -0
- mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/expected.json +44 -0
- mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/stations.json +1 -0
- mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/expected.json +48 -0
- mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/observations.ndjson +4 -0
- mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/cities.xlsx +0 -0
- mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/expected.json +24 -0
- mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/expected.json +27 -0
- mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
- mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/expected.json +27 -0
- mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/README.md +20 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/expected.json +55 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/gfs-2m-temperature.grib2 +0 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/expected.json +54 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/expected.json +54 -0
- mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/hrrr-categorical-rain.grib2 +0 -0
- mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/expected.json +54 -0
- mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
- mostlyright/data_harness/readers/samples.py +582 -0
- mostlyright/data_harness/readers/spreadsheet.py +803 -0
- mostlyright/data_harness/readers/tabular.py +510 -0
- mostlyright/data_harness/recipe.py +5321 -0
- mostlyright/data_harness/repair/__init__.py +78 -0
- mostlyright/data_harness/repair/adapters.py +274 -0
- mostlyright/data_harness/repair/contracts.py +872 -0
- mostlyright/data_harness/repair/coordinator.py +1099 -0
- mostlyright/data_harness/repair/errors.py +16 -0
- mostlyright/data_harness/review.py +2533 -0
- mostlyright/data_harness/rowset.py +283 -0
- mostlyright/data_harness/serving.py +1975 -0
- mostlyright/data_harness/serving_edge.py +590 -0
- mostlyright/data_harness/serving_http.py +1031 -0
- mostlyright/data_harness/session_probes.py +759 -0
- mostlyright/data_harness/signing.py +101 -0
- mostlyright/data_harness/source_discovery.py +898 -0
- mostlyright/data_harness/sources/__init__.py +209 -0
- mostlyright/data_harness/sources/_adapter_steps.py +213 -0
- mostlyright/data_harness/sources/adapters.py +1214 -0
- mostlyright/data_harness/sources/cadence.py +1428 -0
- mostlyright/data_harness/sources/cadence_emission.py +453 -0
- mostlyright/data_harness/sources/cadence_history.py +546 -0
- mostlyright/data_harness/sources/catalog/__init__.py +17 -0
- mostlyright/data_harness/sources/catalog/admission.py +477 -0
- mostlyright/data_harness/sources/catalog/authoring.py +1701 -0
- mostlyright/data_harness/sources/catalog/authoring_policy.py +701 -0
- mostlyright/data_harness/sources/catalog/authoring_shards.py +1217 -0
- mostlyright/data_harness/sources/catalog/bounded_io.py +231 -0
- mostlyright/data_harness/sources/catalog/channel.py +523 -0
- mostlyright/data_harness/sources/catalog/channel_client.py +296 -0
- mostlyright/data_harness/sources/catalog/contracts.py +825 -0
- mostlyright/data_harness/sources/catalog/coverage.py +137 -0
- mostlyright/data_harness/sources/catalog/delta.py +1340 -0
- mostlyright/data_harness/sources/catalog/embedding.py +532 -0
- mostlyright/data_harness/sources/catalog/entry_v2.py +1182 -0
- mostlyright/data_harness/sources/catalog/fill.py +3889 -0
- mostlyright/data_harness/sources/catalog/fill_partitions.py +459 -0
- mostlyright/data_harness/sources/catalog/fill_staging.py +1105 -0
- mostlyright/data_harness/sources/catalog/gating.py +374 -0
- mostlyright/data_harness/sources/catalog/generation_receipt.py +1607 -0
- mostlyright/data_harness/sources/catalog/harvest/__init__.py +7 -0
- mostlyright/data_harness/sources/catalog/harvest/ckan.py +384 -0
- mostlyright/data_harness/sources/catalog/harvest/datagov_v4.py +798 -0
- mostlyright/data_harness/sources/catalog/harvest/protocol.py +964 -0
- mostlyright/data_harness/sources/catalog/harvest/sdmx.py +445 -0
- mostlyright/data_harness/sources/catalog/harvest/stac.py +384 -0
- mostlyright/data_harness/sources/catalog/health.py +447 -0
- mostlyright/data_harness/sources/catalog/hosted_catalog.py +105 -0
- mostlyright/data_harness/sources/catalog/identity_history.py +1549 -0
- mostlyright/data_harness/sources/catalog/neural.py +1618 -0
- mostlyright/data_harness/sources/catalog/packed_catalog.py +2345 -0
- mostlyright/data_harness/sources/catalog/packed_retrieval.py +1517 -0
- mostlyright/data_harness/sources/catalog/packed_writer.py +2802 -0
- mostlyright/data_harness/sources/catalog/query_trace.py +1037 -0
- mostlyright/data_harness/sources/catalog/recommend.py +171 -0
- mostlyright/data_harness/sources/catalog/retrieval.py +230 -0
- mostlyright/data_harness/sources/catalog/retrieval_manifest.py +995 -0
- mostlyright/data_harness/sources/catalog/rights_decisions.py +254 -0
- mostlyright/data_harness/sources/catalog/sealed.py +560 -0
- mostlyright/data_harness/sources/catalog/search.py +230 -0
- mostlyright/data_harness/sources/catalog/streaming_delta.py +1097 -0
- mostlyright/data_harness/sources/catalog/update.py +891 -0
- mostlyright/data_harness/sources/collections.py +815 -0
- mostlyright/data_harness/sources/contracts.py +2223 -0
- mostlyright/data_harness/sources/deletion.py +761 -0
- mostlyright/data_harness/sources/fitness.py +162 -0
- mostlyright/data_harness/sources/governance.py +163 -0
- mostlyright/data_harness/sources/hosted.py +173 -0
- mostlyright/data_harness/sources/integration.py +218 -0
- mostlyright/data_harness/sources/range_reader.py +418 -0
- mostlyright/data_harness/sources/registry.py +514 -0
- mostlyright/data_harness/sources/rights_rule.py +59 -0
- mostlyright/data_harness/sources/source_cadence_vectors.v1.json +1 -0
- mostlyright/data_harness/sources/sports.py +521 -0
- mostlyright/data_harness/sources/stream.py +524 -0
- mostlyright/data_harness/sources/stream_connector.py +418 -0
- mostlyright/data_harness/sources/stream_recorder.py +1404 -0
- mostlyright/data_harness/studio_boundary.py +2019 -0
- mostlyright/data_harness/thin/__init__.py +37 -0
- mostlyright/data_harness/thin/acquire.py +1137 -0
- mostlyright/data_harness/thin/acquire_cancel.py +579 -0
- mostlyright/data_harness/thin/approvals.py +617 -0
- mostlyright/data_harness/thin/commands.py +406 -0
- mostlyright/data_harness/thin/download.py +194 -0
- mostlyright/data_harness/thin/narrative.py +589 -0
- mostlyright/data_harness/thin/parity.py +1070 -0
- mostlyright/data_harness/thin/propose.py +2759 -0
- mostlyright/data_harness/thin/research.py +1663 -0
- mostlyright/data_harness/thin/router.py +924 -0
- mostlyright/data_harness/thin/runs.py +519 -0
- mostlyright/data_harness/thin/session.py +281 -0
- mostlyright/data_harness/thin/stream.py +501 -0
- mostlyright/data_harness/thin/transport.py +187 -0
- mostlyright/data_harness/thin/vocabulary.py +368 -0
- mostlyright/data_harness/thin/workers.py +164 -0
- mostlyright/data_harness/ucum/TABLE-PIN.json +40 -0
- mostlyright/data_harness/ucum/ucum-subset.v1.json +632 -0
- mostlyright/data_harness/unit_flow.py +927 -0
- mostlyright/data_harness/units.py +572 -0
- mostlyright/data_harness/ux/__init__.py +9 -0
- mostlyright/data_harness/ux/approve.py +485 -0
- mostlyright/data_harness/ux/author_yaml.py +597 -0
- mostlyright/data_harness/ux/cloud_auth.py +447 -0
- mostlyright/data_harness/ux/commands/__init__.py +260 -0
- mostlyright/data_harness/ux/commands/approve.py +136 -0
- mostlyright/data_harness/ux/commands/auth.py +744 -0
- mostlyright/data_harness/ux/commands/author.py +79 -0
- mostlyright/data_harness/ux/commands/catalog_author.py +403 -0
- mostlyright/data_harness/ux/commands/catalog_fill.py +523 -0
- mostlyright/data_harness/ux/commands/catalog_harvest.py +545 -0
- mostlyright/data_harness/ux/commands/catalog_publish.py +1838 -0
- mostlyright/data_harness/ux/commands/catalog_search.py +71 -0
- mostlyright/data_harness/ux/commands/catalog_update.py +437 -0
- mostlyright/data_harness/ux/commands/deploy.py +134 -0
- mostlyright/data_harness/ux/commands/deploy_dataset.py +98 -0
- mostlyright/data_harness/ux/commands/deploy_plan.py +105 -0
- mostlyright/data_harness/ux/commands/deploy_status.py +104 -0
- mostlyright/data_harness/ux/commands/diff.py +74 -0
- mostlyright/data_harness/ux/commands/index.py +84 -0
- mostlyright/data_harness/ux/commands/inventory.py +47 -0
- mostlyright/data_harness/ux/commands/list_builds.py +143 -0
- mostlyright/data_harness/ux/commands/login.py +63 -0
- mostlyright/data_harness/ux/commands/peek.py +236 -0
- mostlyright/data_harness/ux/commands/plan_check.py +90 -0
- mostlyright/data_harness/ux/commands/preflight.py +97 -0
- mostlyright/data_harness/ux/commands/record.py +107 -0
- mostlyright/data_harness/ux/commands/review_setup.py +47 -0
- mostlyright/data_harness/ux/commands/search.py +440 -0
- mostlyright/data_harness/ux/commands/show.py +61 -0
- mostlyright/data_harness/ux/commands/whoami.py +37 -0
- mostlyright/data_harness/ux/credential_native.py +551 -0
- mostlyright/data_harness/ux/credential_store.py +1055 -0
- mostlyright/data_harness/ux/credentials.py +631 -0
- mostlyright/data_harness/ux/diffing.py +444 -0
- mostlyright/data_harness/ux/headline.py +671 -0
- mostlyright/data_harness/ux/hosted_acquisition.py +974 -0
- mostlyright/data_harness/ux/hosted_run_status.py +619 -0
- mostlyright/data_harness/ux/inventory.py +427 -0
- mostlyright/data_harness/ux/local_review.py +375 -0
- mostlyright/data_harness/ux/login.py +691 -0
- mostlyright/data_harness/ux/path_kind.py +147 -0
- mostlyright/data_harness/ux/peek.py +1000 -0
- mostlyright/data_harness/ux/plain_file.py +178 -0
- mostlyright/data_harness/ux/plan_check.py +311 -0
- mostlyright/data_harness/ux/preflight.py +918 -0
- mostlyright/data_harness/ux/readers.py +1124 -0
- mostlyright/data_harness/ux/remediation.py +2195 -0
- mostlyright/data_harness/ux/render.py +657 -0
- mostlyright/data_harness/ux/workload.py +1077 -0
- mostlyright/data_harness/viewer.py +3713 -0
- mostlyright/data_harness/visual_run/__init__.py +83 -0
- mostlyright/data_harness/visual_run/authoring.py +235 -0
- mostlyright/data_harness/visual_run/contracts.py +673 -0
- mostlyright/data_harness/visual_run/materialize.py +486 -0
- mostlyright/data_harness/visual_run/observations.py +874 -0
- mostlyright/data_harness/visual_run/query.py +259 -0
- mostlyright/data_harness/visual_run/reducer.py +280 -0
- mostlyright/data_harness/visual_run/sdk.py +892 -0
- mostlyright/data_harness/visual_run/store.py +584 -0
- mostlyright/data_harness/visual_run/transport.py +239 -0
- mostlyright/data_harness/watch.py +2999 -0
- mostlyright_data-0.9.0.dist-info/METADATA +607 -0
- mostlyright_data-0.9.0.dist-info/RECORD +314 -0
- mostlyright_data-0.9.0.dist-info/WHEEL +4 -0
- mostlyright_data-0.9.0.dist-info/entry_points.txt +12 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""Deterministic source-fitness report construction and comparison."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from mostlyright.data_harness.local_contracts import SourceProposal
|
|
8
|
+
from mostlyright.data_harness.sources.contracts import (
|
|
9
|
+
FITNESS_DIMENSIONS,
|
|
10
|
+
EvidenceReference,
|
|
11
|
+
FitnessEvidence,
|
|
12
|
+
FitnessReport,
|
|
13
|
+
RightsEvidence,
|
|
14
|
+
SourceContractError,
|
|
15
|
+
SourceObservation,
|
|
16
|
+
evaluate_obligations,
|
|
17
|
+
)
|
|
18
|
+
from mostlyright.data_harness.sources.rights_rule import rights_disposition
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class FitnessSignals:
|
|
23
|
+
"""Worker-calculated or exact-review inputs for every required dimension."""
|
|
24
|
+
|
|
25
|
+
outcomes: tuple[tuple[str, bool], ...]
|
|
26
|
+
summaries: tuple[tuple[str, str], ...]
|
|
27
|
+
evidence: tuple[EvidenceReference, ...]
|
|
28
|
+
|
|
29
|
+
def __post_init__(self) -> None:
|
|
30
|
+
outcome_keys = [key for key, _ in self.outcomes]
|
|
31
|
+
summary_keys = [key for key, _ in self.summaries]
|
|
32
|
+
if set(outcome_keys) != FITNESS_DIMENSIONS or len(outcome_keys) != len(FITNESS_DIMENSIONS):
|
|
33
|
+
raise SourceContractError(
|
|
34
|
+
"FITNESS_SIGNALS",
|
|
35
|
+
"signals.outcomes",
|
|
36
|
+
"must contain each required fitness dimension exactly once",
|
|
37
|
+
)
|
|
38
|
+
if set(summary_keys) != FITNESS_DIMENSIONS or len(summary_keys) != len(FITNESS_DIMENSIONS):
|
|
39
|
+
raise SourceContractError(
|
|
40
|
+
"FITNESS_SIGNALS",
|
|
41
|
+
"signals.summaries",
|
|
42
|
+
"must contain each required fitness dimension exactly once",
|
|
43
|
+
)
|
|
44
|
+
if not self.evidence:
|
|
45
|
+
raise SourceContractError(
|
|
46
|
+
"FITNESS_EVIDENCE",
|
|
47
|
+
"signals.evidence",
|
|
48
|
+
"fitness requires immutable cited evidence",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def outcome_map(self) -> dict[str, bool]:
|
|
52
|
+
return dict(self.outcomes)
|
|
53
|
+
|
|
54
|
+
def summary_map(self) -> dict[str, str]:
|
|
55
|
+
return dict(self.summaries)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def build_fitness_report(
|
|
59
|
+
*,
|
|
60
|
+
report_id: str,
|
|
61
|
+
question_id: str,
|
|
62
|
+
proposal: SourceProposal,
|
|
63
|
+
observation: SourceObservation,
|
|
64
|
+
rights: RightsEvidence,
|
|
65
|
+
intended_use: str,
|
|
66
|
+
attached_license_artifact_digests: tuple[str, ...],
|
|
67
|
+
signals: FitnessSignals,
|
|
68
|
+
require_timely: bool,
|
|
69
|
+
max_publication_delay_seconds: int,
|
|
70
|
+
) -> FitnessReport:
|
|
71
|
+
"""Build a complete cited report and derive its decision."""
|
|
72
|
+
|
|
73
|
+
if proposal.source_id != observation.source_id:
|
|
74
|
+
raise SourceContractError(
|
|
75
|
+
"SOURCE_BINDING",
|
|
76
|
+
"proposal.source_id",
|
|
77
|
+
"proposal and observation source IDs must match",
|
|
78
|
+
)
|
|
79
|
+
if proposal.observed_at != observation.observed_at:
|
|
80
|
+
raise SourceContractError(
|
|
81
|
+
"OBSERVATION_TIME_BINDING",
|
|
82
|
+
"proposal.observed_at",
|
|
83
|
+
"proposal and worker observation must refer to the same observation time",
|
|
84
|
+
)
|
|
85
|
+
if max_publication_delay_seconds < 0:
|
|
86
|
+
raise SourceContractError(
|
|
87
|
+
"DELAY_LIMIT",
|
|
88
|
+
"max_publication_delay_seconds",
|
|
89
|
+
"must not be negative",
|
|
90
|
+
)
|
|
91
|
+
if rights_disposition(rights.status) == "admit":
|
|
92
|
+
evaluate_obligations(
|
|
93
|
+
rights=rights,
|
|
94
|
+
lifecycle="candidate",
|
|
95
|
+
intended_use=intended_use,
|
|
96
|
+
attached_artifact_digests=attached_license_artifact_digests,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
outcomes = signals.outcome_map()
|
|
100
|
+
summaries = signals.summary_map()
|
|
101
|
+
cited_digests = tuple(item.content_sha256 for item in signals.evidence)
|
|
102
|
+
if rights_disposition(rights.status) != "admit":
|
|
103
|
+
outcomes["rights"] = False
|
|
104
|
+
summaries["rights"] = f"rights status {rights.status} blocks source selection"
|
|
105
|
+
if require_timely and (
|
|
106
|
+
observation.live_status != "live"
|
|
107
|
+
or observation.publication_delay_seconds > max_publication_delay_seconds
|
|
108
|
+
):
|
|
109
|
+
outcomes["liveness"] = False
|
|
110
|
+
outcomes["publication_delay"] = False
|
|
111
|
+
summaries["liveness"] = (
|
|
112
|
+
f"timely source required; observed endpoint is {observation.live_status}"
|
|
113
|
+
)
|
|
114
|
+
summaries["publication_delay"] = (
|
|
115
|
+
f"observed delay {observation.publication_delay_seconds}s exceeds "
|
|
116
|
+
f"{max_publication_delay_seconds}s"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
dimensions = tuple(
|
|
120
|
+
FitnessEvidence(
|
|
121
|
+
dimension=dimension,
|
|
122
|
+
outcome="pass" if outcomes[dimension] else "fail",
|
|
123
|
+
summary=summaries[dimension],
|
|
124
|
+
evidence_digests=cited_digests,
|
|
125
|
+
)
|
|
126
|
+
for dimension in sorted(FITNESS_DIMENSIONS)
|
|
127
|
+
)
|
|
128
|
+
failed = [item.dimension for item in dimensions if item.outcome != "pass"]
|
|
129
|
+
if rights_disposition(rights.status) == "escalate":
|
|
130
|
+
decision = "human_escalation_required"
|
|
131
|
+
reason = "rights are unclear and require human legal/product escalation"
|
|
132
|
+
elif failed:
|
|
133
|
+
decision = "rejected"
|
|
134
|
+
reason = f"failed fitness dimensions: {', '.join(failed)}"
|
|
135
|
+
else:
|
|
136
|
+
decision = "selected"
|
|
137
|
+
reason = "all required fitness dimensions pass"
|
|
138
|
+
return FitnessReport(
|
|
139
|
+
report_id=report_id,
|
|
140
|
+
question_id=question_id,
|
|
141
|
+
source_id=proposal.source_id,
|
|
142
|
+
observed_at=observation.observed_at,
|
|
143
|
+
intended_use=intended_use,
|
|
144
|
+
dimensions=dimensions,
|
|
145
|
+
decision=decision,
|
|
146
|
+
decision_reason=reason,
|
|
147
|
+
rights_digest=rights.digest,
|
|
148
|
+
observation_digest=observation.digest,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def select_one_source(reports: tuple[FitnessReport, ...]) -> FitnessReport:
|
|
153
|
+
"""Require one and only one fully selected source."""
|
|
154
|
+
|
|
155
|
+
selected = tuple(report for report in reports if report.decision == "selected")
|
|
156
|
+
if len(selected) != 1:
|
|
157
|
+
raise SourceContractError(
|
|
158
|
+
"SOURCE_SELECTION",
|
|
159
|
+
"fitness_reports",
|
|
160
|
+
f"expected exactly one selected source, found {len(selected)}",
|
|
161
|
+
)
|
|
162
|
+
return selected[0]
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Pre-persistence classification and governance controls."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
from mostlyright.data_harness.canonical import sha256_bytes
|
|
9
|
+
from mostlyright.data_harness.sources.contracts import SourceContractError
|
|
10
|
+
|
|
11
|
+
# Sized to the hosted contract's normalized-output maximum: the classification scan runs
|
|
12
|
+
# over the exact bytes a hosted acquisition persists, and a year-scale normalized source
|
|
13
|
+
# is gigabytes. The scan stays one bounded pass; measured at ~10 MB/s on digit-heavy
|
|
14
|
+
# tables (the payment-card scan dominates), so the ceiling costs minutes, not hours --
|
|
15
|
+
# the guard is against unbounded input, not against large input.
|
|
16
|
+
MAX_CLASSIFICATION_SCAN_BYTES = 4_294_967_296
|
|
17
|
+
|
|
18
|
+
_SECRET_PATTERNS = (
|
|
19
|
+
("private_key", re.compile(rb"-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----")),
|
|
20
|
+
("aws_access_key", re.compile(rb"\bAKIA[0-9A-Z]{16}\b")),
|
|
21
|
+
("github_token", re.compile(rb"\bgh[pousr]_[A-Za-z0-9_]{30,255}\b")),
|
|
22
|
+
("bearer_token", re.compile(rb"(?i)\bauthorization\s*:\s*bearer\s+\S{16,}")),
|
|
23
|
+
(
|
|
24
|
+
"credential_assignment",
|
|
25
|
+
re.compile(
|
|
26
|
+
rb"""(?ix)
|
|
27
|
+
\b(?:password|passwd|api[_-]?key|secret)\b
|
|
28
|
+
["']?\s*[=:]\s*["']?\S{8,}
|
|
29
|
+
"""
|
|
30
|
+
),
|
|
31
|
+
),
|
|
32
|
+
)
|
|
33
|
+
_HIGHLY_SENSITIVE_PATTERNS = (
|
|
34
|
+
(
|
|
35
|
+
"us_social_security_number",
|
|
36
|
+
re.compile(rb"(?<![0-9])(?:[0-6][0-9]{2}|7[0-6][0-9])-[0-9]{2}-[0-9]{4}(?![0-9])"),
|
|
37
|
+
),
|
|
38
|
+
(
|
|
39
|
+
"health_record_field",
|
|
40
|
+
re.compile(rb"(?i)\b(?:diagnosis|medical_record|patient_id|health_condition)\b"),
|
|
41
|
+
),
|
|
42
|
+
)
|
|
43
|
+
# The boundaries exclude adjacent letters as well as digits: a primary account number is
|
|
44
|
+
# never embedded inside a longer alphanumeric token, while a SHA-256 digest or a hex
|
|
45
|
+
# identifier routinely carries a thirteen-plus digit run that happens to pass Luhn -- the
|
|
46
|
+
# ECB ICP flow's structure digest opens with sixteen such digits, and a digit-only boundary
|
|
47
|
+
# refused a catalog entry over its own evidence digest. A separator-grouped or
|
|
48
|
+
# punctuation-adjacent number still matches, because quotes, commas, and whitespace are not
|
|
49
|
+
# alphanumeric.
|
|
50
|
+
_PAYMENT_CARD_PATTERN = re.compile(rb"(?<![0-9A-Za-z])(?:[0-9][ -]?){13,19}(?![0-9A-Za-z])")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _luhn_valid(value: bytes) -> bool:
|
|
54
|
+
digits = [byte - ord("0") for byte in value if ord("0") <= byte <= ord("9")]
|
|
55
|
+
if not 13 <= len(digits) <= 19:
|
|
56
|
+
return False
|
|
57
|
+
total = 0
|
|
58
|
+
parity = len(digits) % 2
|
|
59
|
+
for index, digit in enumerate(digits):
|
|
60
|
+
if index % 2 == parity:
|
|
61
|
+
digit *= 2
|
|
62
|
+
if digit > 9:
|
|
63
|
+
digit -= 9
|
|
64
|
+
total += digit
|
|
65
|
+
return total % 10 == 0
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True)
|
|
69
|
+
class ClassificationFinding:
|
|
70
|
+
code: str
|
|
71
|
+
byte_offset: int
|
|
72
|
+
|
|
73
|
+
def __post_init__(self) -> None:
|
|
74
|
+
if not self.code or type(self.byte_offset) is not int or self.byte_offset < 0:
|
|
75
|
+
raise SourceContractError(
|
|
76
|
+
"CLASSIFICATION_FINDING",
|
|
77
|
+
"classification.finding",
|
|
78
|
+
"finding code and byte offset must be valid",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass(frozen=True)
|
|
83
|
+
class ClassificationResult:
|
|
84
|
+
content_sha256: str
|
|
85
|
+
classification: str
|
|
86
|
+
admitted: bool
|
|
87
|
+
findings: tuple[ClassificationFinding, ...]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def classify_before_persistence(
|
|
91
|
+
content: bytes,
|
|
92
|
+
*,
|
|
93
|
+
declared_classification: str,
|
|
94
|
+
lawful_basis_declared: bool,
|
|
95
|
+
) -> ClassificationResult:
|
|
96
|
+
"""Scan bounded bytes before an admitted snapshot is persisted."""
|
|
97
|
+
|
|
98
|
+
if not isinstance(content, bytes):
|
|
99
|
+
raise SourceContractError("TYPE", "content", "classification content must be bytes")
|
|
100
|
+
if len(content) > MAX_CLASSIFICATION_SCAN_BYTES:
|
|
101
|
+
raise SourceContractError(
|
|
102
|
+
"CLASSIFICATION_LIMIT",
|
|
103
|
+
"content",
|
|
104
|
+
f"classification input exceeds {MAX_CLASSIFICATION_SCAN_BYTES} bytes",
|
|
105
|
+
)
|
|
106
|
+
if declared_classification not in {"public", "internal", "confidential", "restricted"}:
|
|
107
|
+
raise SourceContractError(
|
|
108
|
+
"CLASSIFICATION",
|
|
109
|
+
"declared_classification",
|
|
110
|
+
"must be public, internal, confidential, or restricted",
|
|
111
|
+
)
|
|
112
|
+
findings: list[ClassificationFinding] = []
|
|
113
|
+
for code, pattern in (*_SECRET_PATTERNS, *_HIGHLY_SENSITIVE_PATTERNS):
|
|
114
|
+
match = pattern.search(content)
|
|
115
|
+
if match:
|
|
116
|
+
findings.append(ClassificationFinding(code=code, byte_offset=match.start()))
|
|
117
|
+
payment_match = next(
|
|
118
|
+
(match for match in _PAYMENT_CARD_PATTERN.finditer(content) if _luhn_valid(match.group())),
|
|
119
|
+
None,
|
|
120
|
+
)
|
|
121
|
+
if payment_match is not None:
|
|
122
|
+
findings.append(
|
|
123
|
+
ClassificationFinding(
|
|
124
|
+
code="payment_card_candidate",
|
|
125
|
+
byte_offset=payment_match.start(),
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
if not lawful_basis_declared:
|
|
129
|
+
findings.append(ClassificationFinding(code="lawful_basis_missing", byte_offset=0))
|
|
130
|
+
admitted = not findings
|
|
131
|
+
return ClassificationResult(
|
|
132
|
+
content_sha256=sha256_bytes(content),
|
|
133
|
+
classification=declared_classification if admitted else "rejected",
|
|
134
|
+
admitted=admitted,
|
|
135
|
+
findings=tuple(findings),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def require_admissible(result: ClassificationResult) -> None:
|
|
140
|
+
if not result.admitted:
|
|
141
|
+
codes = sorted(item.code for item in result.findings)
|
|
142
|
+
raise SourceContractError(
|
|
143
|
+
"DATA_CLASSIFICATION_REJECTED",
|
|
144
|
+
"classification",
|
|
145
|
+
f"content must not persist; findings={codes}",
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def classify_and_require_admissible(
|
|
150
|
+
content: bytes,
|
|
151
|
+
*,
|
|
152
|
+
declared_classification: str,
|
|
153
|
+
lawful_basis_declared: bool,
|
|
154
|
+
) -> ClassificationResult:
|
|
155
|
+
"""Return the sole admissible classification evidence for these exact bytes."""
|
|
156
|
+
|
|
157
|
+
result = classify_before_persistence(
|
|
158
|
+
content,
|
|
159
|
+
declared_classification=declared_classification,
|
|
160
|
+
lawful_basis_declared=lawful_basis_declared,
|
|
161
|
+
)
|
|
162
|
+
require_admissible(result)
|
|
163
|
+
return result
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Studio-hosted public HTTPS adapter.
|
|
2
|
+
|
|
3
|
+
Kept outside ``sources.adapters`` because the generated Studio boundary imports recipe contracts,
|
|
4
|
+
which in turn import the core source package. This leaf module may depend on both without making
|
|
5
|
+
the core adapter import graph cyclic.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from uuid import UUID
|
|
12
|
+
|
|
13
|
+
from mostlyright.data_harness.acquisition.http import seal_content_addressed_snapshot
|
|
14
|
+
from mostlyright.data_harness.canonical import canonical_sha256
|
|
15
|
+
from mostlyright.data_harness.local_contracts import ContractError, validate_source_locator
|
|
16
|
+
from mostlyright.data_harness.sources._adapter_steps import (
|
|
17
|
+
DecodeRecord,
|
|
18
|
+
_authorize_candidate,
|
|
19
|
+
_output,
|
|
20
|
+
)
|
|
21
|
+
from mostlyright.data_harness.sources.adapters import (
|
|
22
|
+
AdapterGovernance,
|
|
23
|
+
_bind_request,
|
|
24
|
+
_https_query,
|
|
25
|
+
)
|
|
26
|
+
from mostlyright.data_harness.sources.collections import COLLECTION_ADAPTER_ID
|
|
27
|
+
from mostlyright.data_harness.sources.contracts import (
|
|
28
|
+
AcquisitionRequest,
|
|
29
|
+
AdapterDescriptor,
|
|
30
|
+
SourceContractError,
|
|
31
|
+
)
|
|
32
|
+
from mostlyright.data_harness.sources.governance import (
|
|
33
|
+
classify_before_persistence,
|
|
34
|
+
require_admissible,
|
|
35
|
+
)
|
|
36
|
+
from mostlyright.data_harness.sources.registry import AcquisitionContext, AcquisitionOutput
|
|
37
|
+
from mostlyright.data_harness.ux.hosted_acquisition import (
|
|
38
|
+
StudioExchange,
|
|
39
|
+
acquire_public_https,
|
|
40
|
+
source_authority_digest,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class HostedPublicHttpsAdapter:
|
|
46
|
+
"""Public HTTPS adapter executed by Studio's credential-free hosted clean room."""
|
|
47
|
+
|
|
48
|
+
exchange: StudioExchange
|
|
49
|
+
workspace_id: UUID
|
|
50
|
+
governance: AdapterGovernance
|
|
51
|
+
descriptor: AdapterDescriptor
|
|
52
|
+
|
|
53
|
+
def __post_init__(self) -> None:
|
|
54
|
+
if self.descriptor.adapter_id == COLLECTION_ADAPTER_ID:
|
|
55
|
+
raise SourceContractError(
|
|
56
|
+
"HOSTED_COLLECTION_UNSUPPORTED",
|
|
57
|
+
"adapter.descriptor",
|
|
58
|
+
"bounded collections require the local attested acquisition route",
|
|
59
|
+
)
|
|
60
|
+
if self.descriptor.source_class not in {"user_url", "user_api", "external_adapter"}:
|
|
61
|
+
raise SourceContractError(
|
|
62
|
+
"ADAPTER_CLASS",
|
|
63
|
+
"adapter.descriptor.source_class",
|
|
64
|
+
"hosted public HTTPS adapter requires user_url, user_api, or external_adapter",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def acquire(
|
|
68
|
+
self,
|
|
69
|
+
request: AcquisitionRequest,
|
|
70
|
+
context: AcquisitionContext,
|
|
71
|
+
) -> AcquisitionOutput:
|
|
72
|
+
_bind_request(request, self.descriptor)
|
|
73
|
+
if request.credential_reference_id is not None:
|
|
74
|
+
raise SourceContractError(
|
|
75
|
+
"AUTHENTICATED_SOURCE_REQUIRES_TRUSTED_GATEWAY",
|
|
76
|
+
"request.credential_reference_id",
|
|
77
|
+
"hosted public acquisition receives no source credential",
|
|
78
|
+
)
|
|
79
|
+
url, data_format, filename = _https_query(request.query)
|
|
80
|
+
try:
|
|
81
|
+
validate_source_locator(url, "https_url", "request.query.url")
|
|
82
|
+
except ContractError as error:
|
|
83
|
+
raise SourceContractError(
|
|
84
|
+
"HTTPS_URL",
|
|
85
|
+
"request.query.url",
|
|
86
|
+
"hosted public acquisition requires a credential-free canonical URL",
|
|
87
|
+
) from error
|
|
88
|
+
_authorize_candidate(request, self.governance)
|
|
89
|
+
query = {
|
|
90
|
+
"url": url,
|
|
91
|
+
"data_format": data_format,
|
|
92
|
+
"filename": filename,
|
|
93
|
+
"reader_pin": None if request.reader_pin is None else dict(request.reader_pin),
|
|
94
|
+
"resource_caps": (
|
|
95
|
+
None if request.resource_caps is None else dict(request.resource_caps)
|
|
96
|
+
),
|
|
97
|
+
"limits": {
|
|
98
|
+
"max_source_bytes": context.max_source_bytes,
|
|
99
|
+
"max_normalized_bytes": (
|
|
100
|
+
context.max_source_bytes
|
|
101
|
+
if context.max_normalized_bytes is None
|
|
102
|
+
else context.max_normalized_bytes
|
|
103
|
+
),
|
|
104
|
+
"max_rows": context.max_rows,
|
|
105
|
+
"max_columns": context.max_columns,
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
acquired = acquire_public_https(
|
|
109
|
+
exchange=self.exchange,
|
|
110
|
+
workspace_id=self.workspace_id,
|
|
111
|
+
source_id=request.source_id,
|
|
112
|
+
source_authority_digest=source_authority_digest(request.source_id, query),
|
|
113
|
+
acquisition_attempt_id=context.attempt_id,
|
|
114
|
+
query=query,
|
|
115
|
+
)
|
|
116
|
+
classification = classify_before_persistence(
|
|
117
|
+
acquired.content,
|
|
118
|
+
declared_classification=self.governance.declared_classification,
|
|
119
|
+
lawful_basis_declared=self.governance.lawful_basis_declared,
|
|
120
|
+
)
|
|
121
|
+
require_admissible(classification)
|
|
122
|
+
snapshot_path = seal_content_addressed_snapshot(
|
|
123
|
+
context.snapshot_root,
|
|
124
|
+
content=acquired.content,
|
|
125
|
+
suffix=data_format,
|
|
126
|
+
)
|
|
127
|
+
decode = None
|
|
128
|
+
if acquired.family_id is not None:
|
|
129
|
+
if acquired.family_version is None or acquired.decode_options_digest is None:
|
|
130
|
+
raise SourceContractError(
|
|
131
|
+
"HOSTED_RESULT_BINDING",
|
|
132
|
+
"hosted.decode",
|
|
133
|
+
"hosted acquisition returned an incomplete Reader identity",
|
|
134
|
+
)
|
|
135
|
+
decode = DecodeRecord(
|
|
136
|
+
fetched_content_sha256=acquired.raw_content_sha256,
|
|
137
|
+
family_id=acquired.family_id,
|
|
138
|
+
family_version=acquired.family_version,
|
|
139
|
+
decode_options_digest=acquired.decode_options_digest,
|
|
140
|
+
flags=acquired.decode_flags,
|
|
141
|
+
)
|
|
142
|
+
transport_envelope = canonical_sha256(
|
|
143
|
+
{
|
|
144
|
+
"schema_version": "hosted-public-https-transport-envelope.v1",
|
|
145
|
+
"hosted_request_digest": acquired.request_digest,
|
|
146
|
+
"crawler_transport_evidence_digest": acquired.transport_evidence_digest,
|
|
147
|
+
"egress_policy_attestation": acquired.egress_policy_attestation,
|
|
148
|
+
"clean_room": {
|
|
149
|
+
"host_platform": "linux-amd64",
|
|
150
|
+
"parser_network_syscalls": "denied",
|
|
151
|
+
"parser_process_creation": "denied",
|
|
152
|
+
"no_new_privs": True,
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
return _output(
|
|
157
|
+
request=request,
|
|
158
|
+
descriptor=self.descriptor,
|
|
159
|
+
governance=self.governance,
|
|
160
|
+
snapshot_path=snapshot_path,
|
|
161
|
+
content=acquired.content,
|
|
162
|
+
data_format=acquired.data_format,
|
|
163
|
+
media_type=acquired.media_type,
|
|
164
|
+
source_uri=acquired.source_url,
|
|
165
|
+
final_uri=acquired.final_url,
|
|
166
|
+
classification=classification.classification,
|
|
167
|
+
transport_evidence_digest=transport_envelope,
|
|
168
|
+
parsed_schema_digest=acquired.parsed_schema_digest,
|
|
169
|
+
decode=decode,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
__all__ = ["HostedPublicHttpsAdapter"]
|