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.
Files changed (314) hide show
  1. mostlyright/data_harness/__init__.py +158 -0
  2. mostlyright/data_harness/acquisition/__init__.py +55 -0
  3. mostlyright/data_harness/acquisition/http.py +2773 -0
  4. mostlyright/data_harness/acquisition/parsing.py +809 -0
  5. mostlyright/data_harness/acquisition/ranges.py +495 -0
  6. mostlyright/data_harness/acquisition/result_download.py +360 -0
  7. mostlyright/data_harness/acquisition/retention_admission.py +248 -0
  8. mostlyright/data_harness/acquisition/sandbox.py +4888 -0
  9. mostlyright/data_harness/acquisition/url_policy.py +530 -0
  10. mostlyright/data_harness/agent_runtime.py +2743 -0
  11. mostlyright/data_harness/assets/logo-ink.svg +31 -0
  12. mostlyright/data_harness/backends/__init__.py +28 -0
  13. mostlyright/data_harness/backends/pandas_backend.py +350 -0
  14. mostlyright/data_harness/backends/polars_backend.py +366 -0
  15. mostlyright/data_harness/backends/protocol.py +124 -0
  16. mostlyright/data_harness/backends/reference.py +83 -0
  17. mostlyright/data_harness/backends/registry.py +55 -0
  18. mostlyright/data_harness/backends/restrictions.py +126 -0
  19. mostlyright/data_harness/canonical.py +333 -0
  20. mostlyright/data_harness/catalog_job.py +625 -0
  21. mostlyright/data_harness/cli.py +5398 -0
  22. mostlyright/data_harness/contracts.py +53 -0
  23. mostlyright/data_harness/coordinator.py +1307 -0
  24. mostlyright/data_harness/deploy.py +924 -0
  25. mostlyright/data_harness/deploy_target.py +312 -0
  26. mostlyright/data_harness/deployment_evidence.py +1067 -0
  27. mostlyright/data_harness/event_presentation.py +576 -0
  28. mostlyright/data_harness/events.py +2152 -0
  29. mostlyright/data_harness/fast_delimited.py +239 -0
  30. mostlyright/data_harness/fleet.py +237 -0
  31. mostlyright/data_harness/formats.py +236 -0
  32. mostlyright/data_harness/governors.py +1163 -0
  33. mostlyright/data_harness/hosted_bootstrap.py +972 -0
  34. mostlyright/data_harness/hosted_crawler.py +1115 -0
  35. mostlyright/data_harness/hosted_crawler_container_smoke.py +351 -0
  36. mostlyright/data_harness/hosted_crawler_fetch.py +423 -0
  37. mostlyright/data_harness/hosted_crawler_job.py +1277 -0
  38. mostlyright/data_harness/hosted_crawler_protocol.py +676 -0
  39. mostlyright/data_harness/hosted_dataset.py +1500 -0
  40. mostlyright/data_harness/hosted_deploy.py +3037 -0
  41. mostlyright/data_harness/hosted_handoff.py +62 -0
  42. mostlyright/data_harness/hosted_ingestion_contract.py +504 -0
  43. mostlyright/data_harness/hosted_ingestion_job.py +356 -0
  44. mostlyright/data_harness/hosted_ingestion_job_smoke.py +40 -0
  45. mostlyright/data_harness/hosted_session_container_smoke.py +194 -0
  46. mostlyright/data_harness/hosted_session_worker.py +3554 -0
  47. mostlyright/data_harness/hosted_session_worker_job_smoke.py +46 -0
  48. mostlyright/data_harness/hosted_worker.py +6784 -0
  49. mostlyright/data_harness/ingestion/__init__.py +56 -0
  50. mostlyright/data_harness/ingestion/contracts.py +461 -0
  51. mostlyright/data_harness/ingestion/faults.py +42 -0
  52. mostlyright/data_harness/ingestion/gcs_store.py +1162 -0
  53. mostlyright/data_harness/ingestion/spool.py +130 -0
  54. mostlyright/data_harness/ingestion/store.py +885 -0
  55. mostlyright/data_harness/key_seam.py +434 -0
  56. mostlyright/data_harness/linux_process_boundary.py +262 -0
  57. mostlyright/data_harness/local_contracts.py +2880 -0
  58. mostlyright/data_harness/local_search/__init__.py +5 -0
  59. mostlyright/data_harness/local_search/build_index.py +1087 -0
  60. mostlyright/data_harness/local_search/contracts.py +920 -0
  61. mostlyright/data_harness/local_search/query_trace.py +266 -0
  62. mostlyright/data_harness/local_search/retrieval.py +700 -0
  63. mostlyright/data_harness/local_search/sealed.py +474 -0
  64. mostlyright/data_harness/local_search/service.py +784 -0
  65. mostlyright/data_harness/nbrender/CONTRACT.md +212 -0
  66. mostlyright/data_harness/nbrender/__init__.py +12 -0
  67. mostlyright/data_harness/nbrender/chrome.py +359 -0
  68. mostlyright/data_harness/nbrender/code_body.py +266 -0
  69. mostlyright/data_harness/nbrender/document.py +407 -0
  70. mostlyright/data_harness/nbrender/frame.py +275 -0
  71. mostlyright/data_harness/nbrender/interactive.py +337 -0
  72. mostlyright/data_harness/nbrender/markdown_body.py +477 -0
  73. mostlyright/data_harness/nbrender/mr_components.py +134 -0
  74. mostlyright/data_harness/nbrender/outputs_data.py +595 -0
  75. mostlyright/data_harness/nbrender/outputs_rich.py +906 -0
  76. mostlyright/data_harness/nbrender/outputs_source.py +260 -0
  77. mostlyright/data_harness/nbrender/outputs_stage.py +176 -0
  78. mostlyright/data_harness/nbrender/outputs_text.py +400 -0
  79. mostlyright/data_harness/nbrender/parse.py +394 -0
  80. mostlyright/data_harness/nbrender/status.py +40 -0
  81. mostlyright/data_harness/nbrender/tokens.py +1295 -0
  82. mostlyright/data_harness/notebook.py +1710 -0
  83. mostlyright/data_harness/offline.py +2049 -0
  84. mostlyright/data_harness/operation_registry.py +1007 -0
  85. mostlyright/data_harness/operator_setup.py +239 -0
  86. mostlyright/data_harness/pipeline.py +6428 -0
  87. mostlyright/data_harness/plan_graph.py +2026 -0
  88. mostlyright/data_harness/preparation/__init__.py +104 -0
  89. mostlyright/data_harness/preparation/contracts.py +1017 -0
  90. mostlyright/data_harness/preparation/engine.py +221 -0
  91. mostlyright/data_harness/preparation/errors.py +14 -0
  92. mostlyright/data_harness/preparation/gates.py +751 -0
  93. mostlyright/data_harness/preparation/joins.py +574 -0
  94. mostlyright/data_harness/preparation/profile.py +384 -0
  95. mostlyright/data_harness/preparation/table.py +217 -0
  96. mostlyright/data_harness/preparation/transforms.py +568 -0
  97. mostlyright/data_harness/progress_events.py +534 -0
  98. mostlyright/data_harness/readers/__init__.py +46 -0
  99. mostlyright/data_harness/readers/containers.py +963 -0
  100. mostlyright/data_harness/readers/contracts.py +542 -0
  101. mostlyright/data_harness/readers/delimited.py +257 -0
  102. mostlyright/data_harness/readers/grib2/__init__.py +33 -0
  103. mostlyright/data_harness/readers/grib2/admission.py +722 -0
  104. mostlyright/data_harness/readers/grib2/decode.py +1009 -0
  105. mostlyright/data_harness/readers/grib2/geometry.py +1133 -0
  106. mostlyright/data_harness/readers/grib2/portable_math.py +501 -0
  107. mostlyright/data_harness/readers/json_tabular.py +485 -0
  108. mostlyright/data_harness/readers/registry.py +514 -0
  109. mostlyright/data_harness/readers/samples/README.md +110 -0
  110. mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/cities.csv.gz +0 -0
  111. mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/expected.json +24 -0
  112. mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/cities.csv.gz +0 -0
  113. mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/expected.json +24 -0
  114. mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/cities.tar +0 -0
  115. mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/expected.json +24 -0
  116. mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/cities.tar +0 -0
  117. mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/expected.json +24 -0
  118. mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/cities.zip +0 -0
  119. mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/expected.json +25 -0
  120. mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  121. mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/expected.json +25 -0
  122. mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  123. mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/expected.json +25 -0
  124. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/cities.csv +3 -0
  125. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/expected.json +23 -0
  126. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/cities.tsv +5 -0
  127. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/expected.json +25 -0
  128. mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/expected.json +30 -0
  129. mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/observations.csv +5 -0
  130. mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/expected.json +44 -0
  131. mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/stations.json +1 -0
  132. mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/expected.json +48 -0
  133. mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/observations.ndjson +4 -0
  134. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/cities.xlsx +0 -0
  135. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/expected.json +24 -0
  136. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/expected.json +27 -0
  137. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  138. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/expected.json +27 -0
  139. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  140. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/README.md +20 -0
  141. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/expected.json +55 -0
  142. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/gfs-2m-temperature.grib2 +0 -0
  143. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/expected.json +54 -0
  144. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  145. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/expected.json +54 -0
  146. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/hrrr-categorical-rain.grib2 +0 -0
  147. mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/expected.json +54 -0
  148. mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  149. mostlyright/data_harness/readers/samples.py +582 -0
  150. mostlyright/data_harness/readers/spreadsheet.py +803 -0
  151. mostlyright/data_harness/readers/tabular.py +510 -0
  152. mostlyright/data_harness/recipe.py +5321 -0
  153. mostlyright/data_harness/repair/__init__.py +78 -0
  154. mostlyright/data_harness/repair/adapters.py +274 -0
  155. mostlyright/data_harness/repair/contracts.py +872 -0
  156. mostlyright/data_harness/repair/coordinator.py +1099 -0
  157. mostlyright/data_harness/repair/errors.py +16 -0
  158. mostlyright/data_harness/review.py +2533 -0
  159. mostlyright/data_harness/rowset.py +283 -0
  160. mostlyright/data_harness/serving.py +1975 -0
  161. mostlyright/data_harness/serving_edge.py +590 -0
  162. mostlyright/data_harness/serving_http.py +1031 -0
  163. mostlyright/data_harness/session_probes.py +759 -0
  164. mostlyright/data_harness/signing.py +101 -0
  165. mostlyright/data_harness/source_discovery.py +898 -0
  166. mostlyright/data_harness/sources/__init__.py +209 -0
  167. mostlyright/data_harness/sources/_adapter_steps.py +213 -0
  168. mostlyright/data_harness/sources/adapters.py +1214 -0
  169. mostlyright/data_harness/sources/cadence.py +1428 -0
  170. mostlyright/data_harness/sources/cadence_emission.py +453 -0
  171. mostlyright/data_harness/sources/cadence_history.py +546 -0
  172. mostlyright/data_harness/sources/catalog/__init__.py +17 -0
  173. mostlyright/data_harness/sources/catalog/admission.py +477 -0
  174. mostlyright/data_harness/sources/catalog/authoring.py +1701 -0
  175. mostlyright/data_harness/sources/catalog/authoring_policy.py +701 -0
  176. mostlyright/data_harness/sources/catalog/authoring_shards.py +1217 -0
  177. mostlyright/data_harness/sources/catalog/bounded_io.py +231 -0
  178. mostlyright/data_harness/sources/catalog/channel.py +523 -0
  179. mostlyright/data_harness/sources/catalog/channel_client.py +296 -0
  180. mostlyright/data_harness/sources/catalog/contracts.py +825 -0
  181. mostlyright/data_harness/sources/catalog/coverage.py +137 -0
  182. mostlyright/data_harness/sources/catalog/delta.py +1340 -0
  183. mostlyright/data_harness/sources/catalog/embedding.py +532 -0
  184. mostlyright/data_harness/sources/catalog/entry_v2.py +1182 -0
  185. mostlyright/data_harness/sources/catalog/fill.py +3889 -0
  186. mostlyright/data_harness/sources/catalog/fill_partitions.py +459 -0
  187. mostlyright/data_harness/sources/catalog/fill_staging.py +1105 -0
  188. mostlyright/data_harness/sources/catalog/gating.py +374 -0
  189. mostlyright/data_harness/sources/catalog/generation_receipt.py +1607 -0
  190. mostlyright/data_harness/sources/catalog/harvest/__init__.py +7 -0
  191. mostlyright/data_harness/sources/catalog/harvest/ckan.py +384 -0
  192. mostlyright/data_harness/sources/catalog/harvest/datagov_v4.py +798 -0
  193. mostlyright/data_harness/sources/catalog/harvest/protocol.py +964 -0
  194. mostlyright/data_harness/sources/catalog/harvest/sdmx.py +445 -0
  195. mostlyright/data_harness/sources/catalog/harvest/stac.py +384 -0
  196. mostlyright/data_harness/sources/catalog/health.py +447 -0
  197. mostlyright/data_harness/sources/catalog/hosted_catalog.py +105 -0
  198. mostlyright/data_harness/sources/catalog/identity_history.py +1549 -0
  199. mostlyright/data_harness/sources/catalog/neural.py +1618 -0
  200. mostlyright/data_harness/sources/catalog/packed_catalog.py +2345 -0
  201. mostlyright/data_harness/sources/catalog/packed_retrieval.py +1517 -0
  202. mostlyright/data_harness/sources/catalog/packed_writer.py +2802 -0
  203. mostlyright/data_harness/sources/catalog/query_trace.py +1037 -0
  204. mostlyright/data_harness/sources/catalog/recommend.py +171 -0
  205. mostlyright/data_harness/sources/catalog/retrieval.py +230 -0
  206. mostlyright/data_harness/sources/catalog/retrieval_manifest.py +995 -0
  207. mostlyright/data_harness/sources/catalog/rights_decisions.py +254 -0
  208. mostlyright/data_harness/sources/catalog/sealed.py +560 -0
  209. mostlyright/data_harness/sources/catalog/search.py +230 -0
  210. mostlyright/data_harness/sources/catalog/streaming_delta.py +1097 -0
  211. mostlyright/data_harness/sources/catalog/update.py +891 -0
  212. mostlyright/data_harness/sources/collections.py +815 -0
  213. mostlyright/data_harness/sources/contracts.py +2223 -0
  214. mostlyright/data_harness/sources/deletion.py +761 -0
  215. mostlyright/data_harness/sources/fitness.py +162 -0
  216. mostlyright/data_harness/sources/governance.py +163 -0
  217. mostlyright/data_harness/sources/hosted.py +173 -0
  218. mostlyright/data_harness/sources/integration.py +218 -0
  219. mostlyright/data_harness/sources/range_reader.py +418 -0
  220. mostlyright/data_harness/sources/registry.py +514 -0
  221. mostlyright/data_harness/sources/rights_rule.py +59 -0
  222. mostlyright/data_harness/sources/source_cadence_vectors.v1.json +1 -0
  223. mostlyright/data_harness/sources/sports.py +521 -0
  224. mostlyright/data_harness/sources/stream.py +524 -0
  225. mostlyright/data_harness/sources/stream_connector.py +418 -0
  226. mostlyright/data_harness/sources/stream_recorder.py +1404 -0
  227. mostlyright/data_harness/studio_boundary.py +2019 -0
  228. mostlyright/data_harness/thin/__init__.py +37 -0
  229. mostlyright/data_harness/thin/acquire.py +1137 -0
  230. mostlyright/data_harness/thin/acquire_cancel.py +579 -0
  231. mostlyright/data_harness/thin/approvals.py +617 -0
  232. mostlyright/data_harness/thin/commands.py +406 -0
  233. mostlyright/data_harness/thin/download.py +194 -0
  234. mostlyright/data_harness/thin/narrative.py +589 -0
  235. mostlyright/data_harness/thin/parity.py +1070 -0
  236. mostlyright/data_harness/thin/propose.py +2759 -0
  237. mostlyright/data_harness/thin/research.py +1663 -0
  238. mostlyright/data_harness/thin/router.py +924 -0
  239. mostlyright/data_harness/thin/runs.py +519 -0
  240. mostlyright/data_harness/thin/session.py +281 -0
  241. mostlyright/data_harness/thin/stream.py +501 -0
  242. mostlyright/data_harness/thin/transport.py +187 -0
  243. mostlyright/data_harness/thin/vocabulary.py +368 -0
  244. mostlyright/data_harness/thin/workers.py +164 -0
  245. mostlyright/data_harness/ucum/TABLE-PIN.json +40 -0
  246. mostlyright/data_harness/ucum/ucum-subset.v1.json +632 -0
  247. mostlyright/data_harness/unit_flow.py +927 -0
  248. mostlyright/data_harness/units.py +572 -0
  249. mostlyright/data_harness/ux/__init__.py +9 -0
  250. mostlyright/data_harness/ux/approve.py +485 -0
  251. mostlyright/data_harness/ux/author_yaml.py +597 -0
  252. mostlyright/data_harness/ux/cloud_auth.py +447 -0
  253. mostlyright/data_harness/ux/commands/__init__.py +260 -0
  254. mostlyright/data_harness/ux/commands/approve.py +136 -0
  255. mostlyright/data_harness/ux/commands/auth.py +744 -0
  256. mostlyright/data_harness/ux/commands/author.py +79 -0
  257. mostlyright/data_harness/ux/commands/catalog_author.py +403 -0
  258. mostlyright/data_harness/ux/commands/catalog_fill.py +523 -0
  259. mostlyright/data_harness/ux/commands/catalog_harvest.py +545 -0
  260. mostlyright/data_harness/ux/commands/catalog_publish.py +1838 -0
  261. mostlyright/data_harness/ux/commands/catalog_search.py +71 -0
  262. mostlyright/data_harness/ux/commands/catalog_update.py +437 -0
  263. mostlyright/data_harness/ux/commands/deploy.py +134 -0
  264. mostlyright/data_harness/ux/commands/deploy_dataset.py +98 -0
  265. mostlyright/data_harness/ux/commands/deploy_plan.py +105 -0
  266. mostlyright/data_harness/ux/commands/deploy_status.py +104 -0
  267. mostlyright/data_harness/ux/commands/diff.py +74 -0
  268. mostlyright/data_harness/ux/commands/index.py +84 -0
  269. mostlyright/data_harness/ux/commands/inventory.py +47 -0
  270. mostlyright/data_harness/ux/commands/list_builds.py +143 -0
  271. mostlyright/data_harness/ux/commands/login.py +63 -0
  272. mostlyright/data_harness/ux/commands/peek.py +236 -0
  273. mostlyright/data_harness/ux/commands/plan_check.py +90 -0
  274. mostlyright/data_harness/ux/commands/preflight.py +97 -0
  275. mostlyright/data_harness/ux/commands/record.py +107 -0
  276. mostlyright/data_harness/ux/commands/review_setup.py +47 -0
  277. mostlyright/data_harness/ux/commands/search.py +440 -0
  278. mostlyright/data_harness/ux/commands/show.py +61 -0
  279. mostlyright/data_harness/ux/commands/whoami.py +37 -0
  280. mostlyright/data_harness/ux/credential_native.py +551 -0
  281. mostlyright/data_harness/ux/credential_store.py +1055 -0
  282. mostlyright/data_harness/ux/credentials.py +631 -0
  283. mostlyright/data_harness/ux/diffing.py +444 -0
  284. mostlyright/data_harness/ux/headline.py +671 -0
  285. mostlyright/data_harness/ux/hosted_acquisition.py +974 -0
  286. mostlyright/data_harness/ux/hosted_run_status.py +619 -0
  287. mostlyright/data_harness/ux/inventory.py +427 -0
  288. mostlyright/data_harness/ux/local_review.py +375 -0
  289. mostlyright/data_harness/ux/login.py +691 -0
  290. mostlyright/data_harness/ux/path_kind.py +147 -0
  291. mostlyright/data_harness/ux/peek.py +1000 -0
  292. mostlyright/data_harness/ux/plain_file.py +178 -0
  293. mostlyright/data_harness/ux/plan_check.py +311 -0
  294. mostlyright/data_harness/ux/preflight.py +918 -0
  295. mostlyright/data_harness/ux/readers.py +1124 -0
  296. mostlyright/data_harness/ux/remediation.py +2195 -0
  297. mostlyright/data_harness/ux/render.py +657 -0
  298. mostlyright/data_harness/ux/workload.py +1077 -0
  299. mostlyright/data_harness/viewer.py +3713 -0
  300. mostlyright/data_harness/visual_run/__init__.py +83 -0
  301. mostlyright/data_harness/visual_run/authoring.py +235 -0
  302. mostlyright/data_harness/visual_run/contracts.py +673 -0
  303. mostlyright/data_harness/visual_run/materialize.py +486 -0
  304. mostlyright/data_harness/visual_run/observations.py +874 -0
  305. mostlyright/data_harness/visual_run/query.py +259 -0
  306. mostlyright/data_harness/visual_run/reducer.py +280 -0
  307. mostlyright/data_harness/visual_run/sdk.py +892 -0
  308. mostlyright/data_harness/visual_run/store.py +584 -0
  309. mostlyright/data_harness/visual_run/transport.py +239 -0
  310. mostlyright/data_harness/watch.py +2999 -0
  311. mostlyright_data-0.9.0.dist-info/METADATA +607 -0
  312. mostlyright_data-0.9.0.dist-info/RECORD +314 -0
  313. mostlyright_data-0.9.0.dist-info/WHEEL +4 -0
  314. mostlyright_data-0.9.0.dist-info/entry_points.txt +12 -0
@@ -0,0 +1,972 @@
1
+ """Workload-identity bootstrap for hosted producer and verifier workers.
2
+
3
+ The launcher is the only process that sees the ambient workload identity response. It exchanges
4
+ that identity for one attempt-scoped Studio bootstrap envelope, validates the complete envelope,
5
+ places each worker input in a separate anonymous descriptor, sanitizes descriptor inheritance and
6
+ the environment, and then replaces itself with the fixed worker entrypoint.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import hashlib
13
+ import os
14
+ import re
15
+ import ssl
16
+ import stat
17
+ import sys
18
+ import tempfile
19
+ from collections.abc import Callable, Mapping, Sequence
20
+ from dataclasses import dataclass
21
+ from datetime import UTC, datetime
22
+ from pathlib import Path
23
+ from typing import Any, NoReturn, Protocol
24
+ from urllib.error import HTTPError
25
+ from urllib.parse import urlencode, urlsplit
26
+ from urllib.request import (
27
+ HTTPRedirectHandler,
28
+ HTTPSHandler,
29
+ ProxyHandler,
30
+ Request,
31
+ build_opener,
32
+ )
33
+ from uuid import UUID
34
+
35
+ from mostlyright.data_harness import pipeline
36
+ from mostlyright.data_harness.canonical import canonical_json_bytes
37
+ from mostlyright.data_harness.governors import (
38
+ CapExceeded,
39
+ CapLedger,
40
+ CapStoreError,
41
+ FileCapStore,
42
+ Reservation,
43
+ )
44
+ from mostlyright.data_harness.hosted_worker import (
45
+ MAX_CAPABILITY_BYTES,
46
+ MAX_DESCRIPTOR_BYTES,
47
+ MAX_SIGNING_KEY_BYTES,
48
+ HostedWorkerError,
49
+ _strict_json,
50
+ parse_capability,
51
+ parse_producer_job,
52
+ parse_signing_key,
53
+ parse_verifier_job,
54
+ )
55
+ from mostlyright.data_harness.local_contracts import GraphTablePlan
56
+
57
+ # The workers have accepted V3 jobs since the Dataset/Table cutover. Bootstrap must speak the
58
+ # matching envelope too: accepting a V2 response here would otherwise let a legacy coordinator
59
+ # re-introduce the child-only identity that the job parser correctly rejects.
60
+ BOOTSTRAP_REQUEST_SCHEMA = "mostlyright-worker-bootstrap-request.v3"
61
+ BOOTSTRAP_RESPONSE_SCHEMA = "mostlyright-worker-bootstrap-response.v3"
62
+ BOOTSTRAP_EXCHANGE_PATH = "/_internal/worker-bootstrap:exchange"
63
+ METADATA_IDENTITY_URL = (
64
+ "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity"
65
+ )
66
+ MAX_IDENTITY_BYTES = 16 * 1024
67
+ MAX_BOOTSTRAP_RESPONSE_BYTES = 768 * 1024
68
+ BOOTSTRAP_TIMEOUT_SECONDS = 10.0
69
+ WORKER_BIN_DIRECTORY = Path("/opt/worker/bin")
70
+ _JWT = re.compile(r"^[A-Za-z0-9_-]{1,8192}\.[A-Za-z0-9_-]{1,8192}\.[A-Za-z0-9_-]{1,8192}$")
71
+ _DISPATCH_QUERY = re.compile(
72
+ r"^dispatch_id=([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$"
73
+ )
74
+ _POLICY_DIGEST = re.compile(r"^sha256:[0-9a-f]{64}$")
75
+ _IMAGE_DIGEST = re.compile(r"^[a-z0-9][a-z0-9._/-]{0,254}@sha256:[0-9a-f]{64}$")
76
+ _POLICY_SELECTION_ENV = "MOSTLYRIGHT_WORKER_POLICY_SELECTION_JSON"
77
+ _POLICY_BINDING_SCHEMA = "mostlyright-worker-policy-binding.v2"
78
+
79
+ #: Where the durable cap counters live. See :func:`admission_ledger` for what setting it and
80
+ #: leaving it unset each actually enforce.
81
+ CAP_STORE_ENV = "MOSTLYRIGHT_CAP_STORE"
82
+
83
+ #: What this deployment declares one run costs, in cost units (one cent each). See
84
+ #: :func:`declared_run_cost_units`: it is the operator's worst-case budget for a run, not a
85
+ #: measurement, and leaving it unset means the monthly spend ceiling cannot trip.
86
+ RUN_COST_UNITS_ENV = "MOSTLYRIGHT_RUN_COST_UNITS"
87
+
88
+
89
+ class BootstrapTransport(Protocol):
90
+ """Minimal redirect-free, proxy-free bootstrap transport."""
91
+
92
+ def request(
93
+ self,
94
+ method: str,
95
+ url: str,
96
+ headers: Mapping[str, str],
97
+ body: bytes | None,
98
+ maximum: int,
99
+ ) -> tuple[int, bytes]: ...
100
+
101
+
102
+ class _NoRedirect(HTTPRedirectHandler):
103
+ def redirect_request(
104
+ self,
105
+ request: Request,
106
+ file_pointer: Any,
107
+ code: int,
108
+ message: str,
109
+ headers: Any,
110
+ new_url: str,
111
+ ) -> None:
112
+ return None
113
+
114
+
115
+ class UrlLibBootstrapTransport:
116
+ """Small stdlib transport with ambient proxies and redirects disabled."""
117
+
118
+ def __init__(self) -> None:
119
+ self._opener = build_opener(
120
+ ProxyHandler({}),
121
+ HTTPSHandler(context=ssl.create_default_context()),
122
+ _NoRedirect(),
123
+ )
124
+
125
+ def request(
126
+ self,
127
+ method: str,
128
+ url: str,
129
+ headers: Mapping[str, str],
130
+ body: bytes | None,
131
+ maximum: int,
132
+ ) -> tuple[int, bytes]:
133
+ request = Request(url, data=body, headers=dict(headers), method=method)
134
+ try:
135
+ response = self._opener.open(request, timeout=BOOTSTRAP_TIMEOUT_SECONDS)
136
+ except HTTPError as error:
137
+ response = error
138
+ with response:
139
+ status = int(response.status)
140
+ content_length = response.headers.get("Content-Length")
141
+ if content_length is not None:
142
+ try:
143
+ declared = int(content_length)
144
+ except ValueError as error:
145
+ raise HostedWorkerError(
146
+ "BOOTSTRAP_TRANSPORT_INVALID",
147
+ "bootstrap response has an invalid content length",
148
+ ) from error
149
+ if declared < 0 or declared > maximum:
150
+ raise HostedWorkerError(
151
+ "BOOTSTRAP_TOO_LARGE",
152
+ "bootstrap response exceeds its byte limit",
153
+ )
154
+ raw = response.read(maximum + 1)
155
+ if len(raw) > maximum:
156
+ raise HostedWorkerError(
157
+ "BOOTSTRAP_TOO_LARGE",
158
+ "bootstrap response exceeds its byte limit",
159
+ )
160
+ return status, raw
161
+
162
+
163
+ @dataclass(frozen=True)
164
+ class BootstrapConfig:
165
+ exchange_url: str
166
+ audience: str
167
+ role: str
168
+ worker_policy_binding: WorkerPolicyBinding | None = None
169
+
170
+
171
+ def _policy_digest(value: Any, label: str) -> str:
172
+ if (
173
+ not isinstance(value, str)
174
+ or not _POLICY_DIGEST.fullmatch(value)
175
+ or value == "sha256:" + "0" * 64
176
+ ):
177
+ raise HostedWorkerError(
178
+ "WORKER_POLICY_BINDING_INVALID",
179
+ f"{label} is not a SHA-256 digest",
180
+ )
181
+ return value
182
+
183
+
184
+ def _image_digest(value: Any, label: str) -> str:
185
+ if (
186
+ not isinstance(value, str)
187
+ or not _IMAGE_DIGEST.fullmatch(value)
188
+ or value.endswith("@sha256:" + "0" * 64)
189
+ ):
190
+ raise HostedWorkerError(
191
+ "WORKER_POLICY_BINDING_INVALID",
192
+ f"{label} is not immutable",
193
+ )
194
+ return value
195
+
196
+
197
+ def parse_worker_policy_binding(value: Any) -> WorkerPolicyBinding:
198
+ """Parse the exact deploy-owned V2 policy-set binding and recompute both digests."""
199
+
200
+ expected = {
201
+ "schema_version",
202
+ "producer_image_digest",
203
+ "verifier_image_digest",
204
+ "validation_policy_digests",
205
+ "policy_set_digest",
206
+ "release_revision_coordinate",
207
+ "selection_id",
208
+ }
209
+ if not isinstance(value, dict) or set(value) != expected:
210
+ raise HostedWorkerError(
211
+ "WORKER_POLICY_BINDING_INVALID",
212
+ "worker policy binding fields are not exact",
213
+ )
214
+ if value["schema_version"] != _POLICY_BINDING_SCHEMA:
215
+ raise HostedWorkerError(
216
+ "WORKER_POLICY_BINDING_INVALID",
217
+ "worker policy binding schema is invalid",
218
+ )
219
+ digests = value["validation_policy_digests"]
220
+ if (
221
+ not isinstance(digests, dict)
222
+ or set(digests) != {"table", "graph"}
223
+ or any(not isinstance(mode, str) for mode in digests)
224
+ ):
225
+ raise HostedWorkerError(
226
+ "WORKER_POLICY_BINDING_INVALID",
227
+ "worker policy digest set must contain exactly table and graph",
228
+ )
229
+ policies = {
230
+ mode: _policy_digest(digests[mode], f"validation_policy_digests.{mode}")
231
+ for mode in ("table", "graph")
232
+ }
233
+ policy_set_digest = _policy_digest(value["policy_set_digest"], "policy_set_digest")
234
+ if "sha256:" + hashlib.sha256(canonical_json_bytes(policies)).hexdigest() != policy_set_digest:
235
+ raise HostedWorkerError(
236
+ "WORKER_POLICY_BINDING_INVALID",
237
+ "policy set digest does not bind the exact table and graph digests",
238
+ )
239
+ selection_id = value["selection_id"]
240
+ if (
241
+ not isinstance(selection_id, str)
242
+ or not re.fullmatch(r"[0-9a-f]{64}", selection_id)
243
+ or selection_id == "0" * 64
244
+ ):
245
+ raise HostedWorkerError(
246
+ "WORKER_POLICY_BINDING_INVALID",
247
+ "selection ID is invalid",
248
+ )
249
+ binding = WorkerPolicyBinding(
250
+ producer_image_digest=_image_digest(
251
+ value["producer_image_digest"], "producer_image_digest"
252
+ ),
253
+ verifier_image_digest=_image_digest(
254
+ value["verifier_image_digest"], "verifier_image_digest"
255
+ ),
256
+ validation_policy_digests=policies,
257
+ policy_set_digest=policy_set_digest,
258
+ release_revision_coordinate=_policy_digest(
259
+ value["release_revision_coordinate"], "release_revision_coordinate"
260
+ ),
261
+ selection_id=selection_id,
262
+ )
263
+ unsigned = binding.to_dict()
264
+ unsigned.pop("selection_id")
265
+ if hashlib.sha256(canonical_json_bytes(unsigned)).hexdigest() != selection_id:
266
+ raise HostedWorkerError(
267
+ "WORKER_POLICY_BINDING_INVALID",
268
+ "selection ID does not bind the exact worker policy selection",
269
+ )
270
+ return binding
271
+
272
+
273
+ def _local_validation_policy_digests() -> dict[str, str]:
274
+ return {
275
+ "table": "sha256:" + pipeline.validation_policy_digest(graph=False),
276
+ "graph": "sha256:" + pipeline.validation_policy_digest(graph=True),
277
+ }
278
+
279
+
280
+ def _require_local_policy_binding(binding: WorkerPolicyBinding) -> None:
281
+ local = _local_validation_policy_digests()
282
+ if binding.validation_policy_digests != local:
283
+ deployed = canonical_json_bytes(binding.validation_policy_digests).decode("utf-8")
284
+ computed = canonical_json_bytes(local).decode("utf-8")
285
+ raise HostedWorkerError(
286
+ "WORKER_POLICY_RUNTIME_MISMATCH",
287
+ "deployed validation policy set "
288
+ f"{deployed} differs from this worker runtime {computed}; "
289
+ "publish and deploy worker images whose receipt carries the runtime policy set",
290
+ )
291
+
292
+
293
+ def _binding_from_environment(environment: Mapping[str, str]) -> WorkerPolicyBinding:
294
+ raw = environment.get(_POLICY_SELECTION_ENV)
295
+ if not isinstance(raw, str):
296
+ raise HostedWorkerError(
297
+ "WORKER_POLICY_BINDING_MISSING",
298
+ "worker policy selection is not configured in this worker template",
299
+ )
300
+ return parse_worker_policy_binding(_strict_json(raw.encode("utf-8"), "worker policy selection"))
301
+
302
+
303
+ @dataclass(frozen=True)
304
+ class WorkerPolicyBinding:
305
+ producer_image_digest: str
306
+ verifier_image_digest: str
307
+ validation_policy_digests: dict[str, str]
308
+ policy_set_digest: str
309
+ release_revision_coordinate: str
310
+ selection_id: str
311
+
312
+ def to_dict(self) -> dict[str, Any]:
313
+ return {
314
+ "schema_version": _POLICY_BINDING_SCHEMA,
315
+ "producer_image_digest": self.producer_image_digest,
316
+ "verifier_image_digest": self.verifier_image_digest,
317
+ "validation_policy_digests": dict(self.validation_policy_digests),
318
+ "policy_set_digest": self.policy_set_digest,
319
+ "release_revision_coordinate": self.release_revision_coordinate,
320
+ "selection_id": self.selection_id,
321
+ }
322
+
323
+
324
+ @dataclass
325
+ class _AnonymousPayload:
326
+ descriptor: int
327
+ handle: Any
328
+
329
+ def close(self) -> None:
330
+ self.handle.close()
331
+
332
+
333
+ def _https_url(value: str, label: str) -> str:
334
+ if not isinstance(value, str) or not 1 <= len(value) <= 2048 or value.endswith("/"):
335
+ raise HostedWorkerError("BOOTSTRAP_CONFIG_INVALID", f"{label} is invalid")
336
+ parsed = urlsplit(value)
337
+ if (
338
+ parsed.scheme != "https"
339
+ or not parsed.hostname
340
+ or parsed.username is not None
341
+ or parsed.password is not None
342
+ or parsed.fragment
343
+ ):
344
+ raise HostedWorkerError(
345
+ "BOOTSTRAP_CONFIG_INVALID",
346
+ f"{label} must be an absolute HTTPS URL without user information or fragment",
347
+ )
348
+ return value
349
+
350
+
351
+ def _v3_exchange_url(value: str) -> str:
352
+ _https_url(value, "exchange_url")
353
+ parsed = urlsplit(value)
354
+ matched = _DISPATCH_QUERY.fullmatch(parsed.query)
355
+ if parsed.path != BOOTSTRAP_EXCHANGE_PATH or matched is None:
356
+ raise HostedWorkerError(
357
+ "BOOTSTRAP_CONFIG_INVALID",
358
+ "exchange_url must use the exact bootstrap callback path "
359
+ f"{BOOTSTRAP_EXCHANGE_PATH} with one canonical UUIDv5 dispatch_id",
360
+ )
361
+ dispatch_id = matched.group(1)
362
+ try:
363
+ parsed_dispatch_id = UUID(dispatch_id)
364
+ except ValueError as error:
365
+ raise HostedWorkerError(
366
+ "BOOTSTRAP_CONFIG_INVALID",
367
+ "exchange_url dispatch_id must be a canonical UUIDv5",
368
+ ) from error
369
+ if parsed_dispatch_id.version != 5 or str(parsed_dispatch_id) != dispatch_id:
370
+ raise HostedWorkerError(
371
+ "BOOTSTRAP_CONFIG_INVALID",
372
+ "exchange_url dispatch_id must be a canonical UUIDv5",
373
+ )
374
+ return value
375
+
376
+
377
+ def _metadata_identity(
378
+ transport: BootstrapTransport,
379
+ audience: str,
380
+ ) -> bytearray:
381
+ query = urlencode({"audience": audience, "format": "full"})
382
+ status, raw = transport.request(
383
+ "GET",
384
+ f"{METADATA_IDENTITY_URL}?{query}",
385
+ {"Metadata-Flavor": "Google"},
386
+ None,
387
+ MAX_IDENTITY_BYTES,
388
+ )
389
+ if status != 200:
390
+ raise HostedWorkerError(
391
+ "WORKLOAD_IDENTITY_UNAVAILABLE",
392
+ f"metadata identity endpoint returned HTTP {status}",
393
+ )
394
+ try:
395
+ token = raw.decode("ascii")
396
+ except UnicodeDecodeError as error:
397
+ raise HostedWorkerError(
398
+ "WORKLOAD_IDENTITY_INVALID",
399
+ "metadata identity response is not ASCII",
400
+ ) from error
401
+ if not _JWT.fullmatch(token):
402
+ raise HostedWorkerError(
403
+ "WORKLOAD_IDENTITY_INVALID",
404
+ "metadata identity response is not a compact JWT",
405
+ )
406
+ return bytearray(raw)
407
+
408
+
409
+ def _exchange(
410
+ config: BootstrapConfig,
411
+ transport: BootstrapTransport,
412
+ identity: bytearray,
413
+ ) -> dict[str, Any]:
414
+ body = canonical_json_bytes(
415
+ {
416
+ "schema_version": BOOTSTRAP_REQUEST_SCHEMA,
417
+ "role": config.role,
418
+ }
419
+ )
420
+ status, raw = transport.request(
421
+ "POST",
422
+ config.exchange_url,
423
+ {
424
+ "Accept": "application/json",
425
+ "Authorization": "Bearer " + identity.decode("ascii"),
426
+ "Content-Type": "application/json",
427
+ },
428
+ body,
429
+ MAX_BOOTSTRAP_RESPONSE_BYTES,
430
+ )
431
+ if status != 200:
432
+ raise HostedWorkerError(
433
+ "BOOTSTRAP_EXCHANGE_FAILED",
434
+ f"Studio bootstrap exchange returned HTTP {status}",
435
+ )
436
+ value = _strict_json(raw, "bootstrap response")
437
+ if not isinstance(value, dict):
438
+ raise HostedWorkerError(
439
+ "BOOTSTRAP_RESPONSE_INVALID",
440
+ "bootstrap response must be an object",
441
+ )
442
+ return value
443
+
444
+
445
+ def _require_worker_policy_preflight(
446
+ *,
447
+ job_wire: Any,
448
+ job: Any,
449
+ response_preflight: Any,
450
+ binding: WorkerPolicyBinding,
451
+ ) -> None:
452
+ """Bind the deployed selection, Studio response, and parsed recipe mode before execution."""
453
+
454
+ if not isinstance(job_wire, dict):
455
+ raise HostedWorkerError(
456
+ "WORKER_POLICY_BINDING_MISMATCH",
457
+ "bootstrap job is not an object",
458
+ )
459
+ job_preflight = job_wire.get("worker_policy_preflight")
460
+ if not isinstance(job_preflight, dict) or response_preflight != job_preflight:
461
+ raise HostedWorkerError(
462
+ "WORKER_POLICY_BINDING_MISMATCH",
463
+ "Studio bootstrap response and job do not carry one exact policy preflight",
464
+ )
465
+ expected = {
466
+ "schema_version",
467
+ "workspace_id",
468
+ "dataset_id",
469
+ "table_plan_id",
470
+ "table_recipe_id",
471
+ "recipe_version",
472
+ "recipe_digest",
473
+ "builder_image_digest",
474
+ "verifier_image_digest",
475
+ "policy_mode",
476
+ "validation_policy_digest",
477
+ "validation_policy_digests",
478
+ "policy_set_digest",
479
+ "selection_fence",
480
+ "selection_id",
481
+ }
482
+ fields = set(job_preflight)
483
+ if (
484
+ fields not in (expected, expected | {"table_id"})
485
+ or job_preflight.get("schema_version") != "3.0.0"
486
+ ):
487
+ raise HostedWorkerError(
488
+ "WORKER_POLICY_BINDING_MISMATCH",
489
+ "worker policy preflight fields are not exact",
490
+ )
491
+ mode = "graph" if isinstance(job.documents.recipe.transform_plan, GraphTablePlan) else "table"
492
+ expected_scope = {
493
+ "workspace_id": str(job.workspace_id),
494
+ "dataset_id": str(job.dataset_id),
495
+ "table_plan_id": str(job.table_plan_id),
496
+ "table_recipe_id": job.table_recipe_id,
497
+ "recipe_version": job.recipe_version,
498
+ "recipe_digest": job.documents.recipe_digest,
499
+ }
500
+ if any(job_preflight.get(name) != value for name, value in expected_scope.items()) or (
501
+ "table_id" in job_preflight and job_preflight["table_id"] != str(job.table_id)
502
+ ):
503
+ raise HostedWorkerError(
504
+ "WORKER_POLICY_BINDING_MISMATCH",
505
+ "worker policy preflight scope differs from the bootstrap job",
506
+ )
507
+ static = {
508
+ "builder_image_digest": binding.producer_image_digest,
509
+ "verifier_image_digest": binding.verifier_image_digest,
510
+ "validation_policy_digests": binding.validation_policy_digests,
511
+ "policy_set_digest": binding.policy_set_digest,
512
+ "selection_fence": binding.release_revision_coordinate,
513
+ "selection_id": binding.selection_id,
514
+ "policy_mode": mode,
515
+ "validation_policy_digest": binding.validation_policy_digests[mode],
516
+ }
517
+ mismatched = [name for name, value in static.items() if job_preflight.get(name) != value]
518
+ if mismatched:
519
+ raise HostedWorkerError(
520
+ "WORKER_POLICY_BINDING_MISMATCH",
521
+ "worker policy preflight differs from the deployed selection: " + ",".join(mismatched),
522
+ )
523
+
524
+
525
+ def _validate_bootstrap_response(
526
+ value: dict[str, Any],
527
+ role: str,
528
+ *,
529
+ worker_policy_binding: WorkerPolicyBinding | None = None,
530
+ ) -> tuple[dict[str, bytes], Any]:
531
+ """Return the worker payload bytes and the parsed job the validation already produced.
532
+
533
+ The parsed job is returned rather than discarded because the admission gate needs the tenant
534
+ coordinate out of it, and reparsing to get it would mean two parses that could disagree.
535
+ """
536
+
537
+ common = {"schema_version", "role", "job", "capability"}
538
+ expected = common if role == "producer" else common | {"signing_key"}
539
+ if worker_policy_binding is not None:
540
+ expected = expected | {"worker_policy_preflight"}
541
+ if set(value) != expected:
542
+ raise HostedWorkerError(
543
+ "BOOTSTRAP_RESPONSE_INVALID",
544
+ "bootstrap response fields are not exact for the requested role",
545
+ )
546
+ if value["schema_version"] != BOOTSTRAP_RESPONSE_SCHEMA or value["role"] != role:
547
+ raise HostedWorkerError(
548
+ "BOOTSTRAP_RESPONSE_INVALID",
549
+ "bootstrap response schema or role mismatches",
550
+ )
551
+
552
+ job_wire = value["job"]
553
+ if not isinstance(job_wire, dict):
554
+ raise HostedWorkerError(
555
+ "BOOTSTRAP_RESPONSE_INVALID",
556
+ "bootstrap job must be an object",
557
+ )
558
+ job = dict(job_wire)
559
+ if worker_policy_binding is not None:
560
+ job.pop("worker_policy_preflight", None)
561
+ capability = value["capability"]
562
+ if not isinstance(capability, str):
563
+ raise HostedWorkerError(
564
+ "BOOTSTRAP_RESPONSE_INVALID",
565
+ "bootstrap capability must be a string",
566
+ )
567
+ capability_raw = capability.encode("ascii", errors="strict")
568
+ if len(capability_raw) > MAX_CAPABILITY_BYTES:
569
+ raise HostedWorkerError("BOOTSTRAP_TOO_LARGE", "capability exceeds its byte limit")
570
+ parse_capability(capability_raw)
571
+ job_raw = canonical_json_bytes(job)
572
+ if len(job_raw) > MAX_DESCRIPTOR_BYTES:
573
+ raise HostedWorkerError("BOOTSTRAP_TOO_LARGE", "worker job exceeds its byte limit")
574
+ if role == "producer":
575
+ parsed_job = parse_producer_job(job)
576
+ if worker_policy_binding is not None:
577
+ _require_worker_policy_preflight(
578
+ job_wire=job_wire,
579
+ job=parsed_job,
580
+ response_preflight=value["worker_policy_preflight"],
581
+ binding=worker_policy_binding,
582
+ )
583
+ return {"job": job_raw, "capability": capability_raw}, parsed_job
584
+
585
+ parsed_job = parse_verifier_job(job)
586
+ if worker_policy_binding is not None:
587
+ _require_worker_policy_preflight(
588
+ job_wire=job_wire,
589
+ job=parsed_job,
590
+ response_preflight=value["worker_policy_preflight"],
591
+ binding=worker_policy_binding,
592
+ )
593
+ signing_key = value["signing_key"]
594
+ parse_signing_key(signing_key)
595
+ signing_raw = canonical_json_bytes(signing_key)
596
+ if len(signing_raw) > MAX_SIGNING_KEY_BYTES:
597
+ raise HostedWorkerError("BOOTSTRAP_TOO_LARGE", "signing key exceeds its byte limit")
598
+ return {
599
+ "job": job_raw,
600
+ "capability": capability_raw,
601
+ "signing_key": signing_raw,
602
+ }, parsed_job
603
+
604
+
605
+ def _anonymous_payload(raw: bytes, label: str) -> _AnonymousPayload:
606
+ if hasattr(os, "memfd_create"):
607
+ descriptor = os.memfd_create( # type: ignore[attr-defined]
608
+ f"mostlyright-{label}",
609
+ flags=getattr(os, "MFD_CLOEXEC", 0),
610
+ )
611
+ handle = os.fdopen(descriptor, "w+b", closefd=True)
612
+ else:
613
+ # macOS development fallback. TemporaryFile is opened mode 0600 and immediately unlinked;
614
+ # production Linux images use memfd_create and never materialize the payload on disk.
615
+ handle = tempfile.TemporaryFile(mode="w+b", prefix=f"mostlyright-{label}-")
616
+ descriptor = handle.fileno()
617
+ handle.write(raw)
618
+ handle.flush()
619
+ handle.seek(0)
620
+ os.set_inheritable(descriptor, True)
621
+ return _AnonymousPayload(descriptor=descriptor, handle=handle)
622
+
623
+
624
+ def _validate_worker_executable(
625
+ executable: Path,
626
+ role: str,
627
+ *,
628
+ chain_root: Path = Path("/"),
629
+ trusted_uid: int = 0,
630
+ ) -> str:
631
+ """Validate one fixed worker executable without following mutable path indirection."""
632
+
633
+ name = f"mr-data-{role}"
634
+ if role not in {"producer", "verifier"} or executable.name != name:
635
+ raise HostedWorkerError(
636
+ "WORKER_EXECUTABLE_UNAVAILABLE",
637
+ f"fixed {name} entrypoint is invalid",
638
+ )
639
+ if (
640
+ not executable.is_absolute()
641
+ or not chain_root.is_absolute()
642
+ or any(part in {".", ".."} for part in executable.parts)
643
+ ):
644
+ raise HostedWorkerError(
645
+ "WORKER_EXECUTABLE_UNAVAILABLE",
646
+ f"fixed {name} entrypoint path is invalid",
647
+ )
648
+ try:
649
+ relative = executable.relative_to(chain_root)
650
+ except ValueError as error:
651
+ raise HostedWorkerError(
652
+ "WORKER_EXECUTABLE_UNAVAILABLE",
653
+ f"fixed {name} entrypoint escapes its trusted path chain",
654
+ ) from error
655
+
656
+ current = chain_root
657
+ components = (
658
+ current,
659
+ *(current / Path(*relative.parts[:index]) for index in range(1, len(relative.parts) + 1)),
660
+ )
661
+ for index, component in enumerate(components):
662
+ try:
663
+ metadata = component.lstat()
664
+ except OSError as error:
665
+ raise HostedWorkerError(
666
+ "WORKER_EXECUTABLE_UNAVAILABLE",
667
+ f"fixed {name} entrypoint is not installed",
668
+ ) from error
669
+ is_last = index == len(components) - 1
670
+ if stat.S_ISLNK(metadata.st_mode):
671
+ raise HostedWorkerError(
672
+ "WORKER_EXECUTABLE_UNSAFE",
673
+ f"fixed {name} entrypoint path contains a symbolic link",
674
+ )
675
+ if metadata.st_mode & (stat.S_IWGRP | stat.S_IWOTH):
676
+ raise HostedWorkerError(
677
+ "WORKER_EXECUTABLE_UNSAFE",
678
+ f"fixed {name} entrypoint path is group or world writable",
679
+ )
680
+ if metadata.st_uid != trusted_uid:
681
+ raise HostedWorkerError(
682
+ "WORKER_EXECUTABLE_UNSAFE",
683
+ f"fixed {name} entrypoint path is not owned by the trusted uid",
684
+ )
685
+ if (is_last and not stat.S_ISREG(metadata.st_mode)) or (
686
+ not is_last and not stat.S_ISDIR(metadata.st_mode)
687
+ ):
688
+ raise HostedWorkerError(
689
+ "WORKER_EXECUTABLE_UNSAFE",
690
+ f"fixed {name} entrypoint path has an invalid file type",
691
+ )
692
+ if not os.access(executable, os.X_OK):
693
+ raise HostedWorkerError(
694
+ "WORKER_EXECUTABLE_UNAVAILABLE",
695
+ f"fixed {name} entrypoint is not executable",
696
+ )
697
+ return str(executable)
698
+
699
+
700
+ def _worker_executable(role: str) -> str:
701
+ """Resolve only the immutable container installation coordinate."""
702
+
703
+ return _validate_worker_executable(WORKER_BIN_DIRECTORY / f"mr-data-{role}", role)
704
+
705
+
706
+ def _worker_argv(
707
+ executable: str, role: str, payloads: Mapping[str, _AnonymousPayload]
708
+ ) -> list[str]:
709
+ arguments = [
710
+ executable,
711
+ "--job-fd",
712
+ str(payloads["job"].descriptor),
713
+ "--capability-fd",
714
+ str(payloads["capability"].descriptor),
715
+ ]
716
+ if role == "verifier":
717
+ arguments.extend(
718
+ [
719
+ "--signing-key-fd",
720
+ str(payloads["signing_key"].descriptor),
721
+ ]
722
+ )
723
+ return arguments
724
+
725
+
726
+ def _restrict_inherited_descriptors(allowed: set[int]) -> list[int]:
727
+ directory = next(
728
+ (Path(candidate) for candidate in ("/proc/self/fd", "/dev/fd") if Path(candidate).is_dir()),
729
+ None,
730
+ )
731
+ if directory is None:
732
+ raise HostedWorkerError(
733
+ "DESCRIPTOR_SANITIZATION_UNAVAILABLE",
734
+ "cannot enumerate descriptors before worker process replacement",
735
+ )
736
+ changed: list[int] = []
737
+ for entry in directory.iterdir():
738
+ try:
739
+ descriptor = int(entry.name)
740
+ except ValueError:
741
+ continue
742
+ if descriptor <= 2 or descriptor in allowed:
743
+ continue
744
+ try:
745
+ if os.get_inheritable(descriptor):
746
+ os.set_inheritable(descriptor, False)
747
+ changed.append(descriptor)
748
+ except OSError:
749
+ # The enumeration directory itself, or another concurrently closed descriptor, can
750
+ # disappear between listing and inspection. It cannot survive exec in that state.
751
+ continue
752
+ return changed
753
+
754
+
755
+ def _restore_inherited_descriptors(descriptors: Sequence[int]) -> None:
756
+ for descriptor in descriptors:
757
+ try:
758
+ os.set_inheritable(descriptor, True)
759
+ except OSError:
760
+ continue
761
+
762
+
763
+ def admission_ledger() -> CapLedger:
764
+ """Build the workspace admission ledger.
765
+
766
+ ``MOSTLYRIGHT_CAP_STORE`` must name shared POSIX storage for monthly limits to persist across
767
+ job executions. The store requires advisory locking and atomic rename and must be initialized
768
+ before use. Without it, counters are process-local. Admission charges the run before execution.
769
+
770
+ Spend uses ``MOSTLYRIGHT_RUN_COST_UNITS``. The ledger uses ``CapPolicy`` defaults because this
771
+ entry point receives no per-workspace policy. It does not persist concurrency reservations.
772
+ """
773
+
774
+ configured = os.environ.get(CAP_STORE_ENV)
775
+ if configured:
776
+ return CapLedger(store=FileCapStore(Path(configured)), subject="workspace")
777
+ return CapLedger(subject="workspace")
778
+
779
+
780
+ def _tenant_coordinate(workspace_id: Any) -> str:
781
+ """Render a workspace id into the 16-lowercase-hex coordinate the cap ledger counts by.
782
+
783
+ The tenant coordinate on this path is ``workspace_id``, taken from the validated bootstrap job.
784
+ It is deliberately NOT an API key id: ``key_id`` in this repository names the attestation
785
+ signing key (``hosted_worker.SigningKey.key_id``), which is a different concept, and the
786
+ customer API key never reaches this process at all (see ``key_seam.CONSUMER_CALL_SITE``).
787
+
788
+ The ledger's coordinate shape is cloud's ``hashKeyId`` shape — the first 8 bytes of a SHA-256
789
+ digest, hex-encoded — and that shape check is what makes it impossible to file a raw credential
790
+ where an identifier belongs. The same derivation is applied to the workspace id here, so the
791
+ check still holds. The honest consequence: these counters are per workspace, so they do not
792
+ line up with cloud's ``usage_events.key_id`` rows, which are per API key. This ledger bounds a
793
+ workspace's runs; it is not the billing attribution record.
794
+ """
795
+
796
+ return hashlib.sha256(str(workspace_id).encode("utf-8")).hexdigest()[:16]
797
+
798
+
799
+ def declared_run_cost_units() -> int:
800
+ """Return the per-run spend this deployment declares, in cost units. Zero when undeclared.
801
+
802
+ What this number is, stated plainly because the alternative reading is flattering and wrong:
803
+ it is a **declared budget, not a measurement**. This seam cannot observe what a run costs. It
804
+ hands the process to the worker with ``execve`` and never regains control, so there is no
805
+ moment here at which a real bill exists to record. What it can do is charge the monthly spend
806
+ ledger the amount the operator says one run is worth at worst, which is how admission-time
807
+ spend control works everywhere: reserve the worst case, admit only if it fits.
808
+
809
+ ``MOSTLYRIGHT_RUN_COST_UNITS`` is therefore set on the worker job specification beside
810
+ ``MOSTLYRIGHT_CAP_STORE``, and the two have the same character: they are not "on" and "slightly
811
+ less on".
812
+
813
+ - **Unset** — every run is charged zero, so the monthly spend ceiling in
814
+ :class:`governors.CapPolicy` cannot trip no matter how many runs are admitted. Spend is
815
+ bounded only by the cloud project's own budget ceiling. This is a real gap, not a
816
+ degraded-but-safe default. What still holds is the monthly run allowance, and only where
817
+ ``MOSTLYRIGHT_CAP_STORE`` names shared storage (:func:`admission_ledger`); the concurrency
818
+ cap does not bind in this topology at all. With neither variable set, no cap here bounds
819
+ anything across executions.
820
+ - **Set to a whole number of cost units** — each admitted run reserves that much against the
821
+ monthly ceiling before it starts and commits the same amount when it does, so the ceiling
822
+ binds at ``max_monthly_cost_units / declared`` runs.
823
+
824
+ A value that cannot be read as a whole number of cost units refuses the run rather than
825
+ defaulting to zero, for the same reason an unreadable cap store refuses it: "this budget is
826
+ misconfigured" and "this run is free" must never be the same answer.
827
+ """
828
+
829
+ configured = os.environ.get(RUN_COST_UNITS_ENV)
830
+ if configured is None or configured == "":
831
+ return 0
832
+ # str.isdigit is the check rather than int(): it refuses the signs, the underscores, the
833
+ # surrounding whitespace and the unicode digits that int() would otherwise accept and turn
834
+ # into a number nobody wrote.
835
+ if not configured.isascii() or not configured.isdigit():
836
+ raise HostedWorkerError(
837
+ "ADMISSION_REFUSED",
838
+ f"{RUN_COST_UNITS_ENV} must be a whole number of cost units, zero or more",
839
+ )
840
+ return int(configured)
841
+
842
+
843
+ def _reserve_run_slot(ledger: CapLedger, job: Any, cost_units: int) -> Reservation:
844
+ """Hold a slot for this run, or refuse the run before anything is allocated for it."""
845
+
846
+ coordinate = _tenant_coordinate(getattr(job, "workspace_id", None))
847
+ try:
848
+ return ledger.reserve(
849
+ coordinate,
850
+ now=datetime.now(UTC),
851
+ estimated_cost_units=cost_units,
852
+ )
853
+ except CapExceeded as error:
854
+ # A cap breach is the product working: it carries the cap that was reached and the two
855
+ # numbers behind it, in plain language, and no credential.
856
+ raise HostedWorkerError("ADMISSION_REFUSED", str(error)) from error
857
+ except CapStoreError as error:
858
+ # Fail closed. Counters that cannot be read are not counters that say zero.
859
+ raise HostedWorkerError("ADMISSION_REFUSED", str(error)) from error
860
+
861
+
862
+ def launch(
863
+ config: BootstrapConfig,
864
+ *,
865
+ transport: BootstrapTransport | None = None,
866
+ execve: Callable[[str, Sequence[str], Mapping[str, str]], Any] = os.execve,
867
+ ledger: CapLedger | None = None,
868
+ _test_executable_resolver: Callable[[str], str] | None = None,
869
+ ) -> NoReturn:
870
+ """Exchange ambient identity, admit the run against its caps, and replace this process.
871
+
872
+ ``ledger`` is a test seam and nothing else: when it is None the real admission ledger is built
873
+ here. The gate itself is unconditional. There is no flag that turns it off, because a cap a
874
+ caller can switch off is not a cap.
875
+ """
876
+
877
+ if config.role not in {"producer", "verifier"}:
878
+ raise HostedWorkerError("BOOTSTRAP_CONFIG_INVALID", "role must be producer or verifier")
879
+ _v3_exchange_url(config.exchange_url)
880
+ _https_url(config.audience, "audience")
881
+ if config.worker_policy_binding is not None:
882
+ _require_local_policy_binding(config.worker_policy_binding)
883
+ client = transport or UrlLibBootstrapTransport()
884
+ identity = _metadata_identity(client, config.audience)
885
+ try:
886
+ response = _exchange(config, client, identity)
887
+ finally:
888
+ identity[:] = b"\x00" * len(identity)
889
+ raw_payloads, job = _validate_bootstrap_response(
890
+ response,
891
+ config.role,
892
+ worker_policy_binding=config.worker_policy_binding,
893
+ )
894
+
895
+ # The gate sits here, at the first moment the tenant coordinate is known and before anything
896
+ # is allocated for the run: a refused run creates no descriptor, holds no memory, and above
897
+ # all never reaches execve.
898
+ if ledger is None:
899
+ try:
900
+ ledger = admission_ledger()
901
+ except CapStoreError as error:
902
+ raise HostedWorkerError("ADMISSION_REFUSED", str(error)) from error
903
+ cost_units = declared_run_cost_units()
904
+ reservation = _reserve_run_slot(ledger, job, cost_units)
905
+
906
+ descriptors: dict[str, _AnonymousPayload] = {}
907
+ # Everything from here to the commit runs inside the reservation's block, so any fault —
908
+ # a descriptor that cannot be created, an entrypoint that fails validation — gives the slot
909
+ # back instead of leaving it held by a run that never started.
910
+ with reservation:
911
+ try:
912
+ for name, raw in raw_payloads.items():
913
+ mutable = bytearray(raw)
914
+ try:
915
+ descriptors[name] = _anonymous_payload(bytes(mutable), name)
916
+ finally:
917
+ mutable[:] = b"\x00" * len(mutable)
918
+ # Production has no executable-resolution injection seam. Tests may supply an explicit
919
+ # trusted stand-in because the fixed /opt/worker installation is created only in images.
920
+ executable = (
921
+ _worker_executable(config.role)
922
+ if _test_executable_resolver is None
923
+ else _test_executable_resolver(config.role)
924
+ )
925
+ arguments = _worker_argv(executable, config.role, descriptors)
926
+ environment = {
927
+ "PATH": "/opt/worker/bin:/usr/local/bin:/usr/bin:/bin",
928
+ "PYTHONDONTWRITEBYTECODE": "1",
929
+ "PYTHONUNBUFFERED": "1",
930
+ }
931
+ allowed = {payload.descriptor for payload in descriptors.values()}
932
+ changed = _restrict_inherited_descriptors(allowed)
933
+ try:
934
+ # Committed before the call, never after: execve replaces this process image and
935
+ # does not return, so an "after" that would record the run does not exist. A
936
+ # commit written below the call would never run and every admitted run would hold
937
+ # its slot forever. The committed amount is the declared budget rather than a
938
+ # measured bill for the same reason: after this line there is no process left here
939
+ # to measure anything with. See declared_run_cost_units.
940
+ reservation.commit(actual_cost_units=cost_units)
941
+ execve(executable, arguments, environment)
942
+ finally:
943
+ _restore_inherited_descriptors(changed)
944
+ finally:
945
+ for payload in descriptors.values():
946
+ payload.close()
947
+ raise HostedWorkerError("WORKER_EXEC_FAILED", "worker process replacement returned")
948
+
949
+
950
+ def main(argv: Sequence[str] | None = None) -> int:
951
+ parser = argparse.ArgumentParser()
952
+ parser.add_argument("--exchange-url", required=True)
953
+ parser.add_argument("--audience", required=True)
954
+ parser.add_argument("--role", required=True, choices=("producer", "verifier"))
955
+ arguments = parser.parse_args(argv)
956
+ try:
957
+ launch(
958
+ BootstrapConfig(
959
+ exchange_url=arguments.exchange_url,
960
+ audience=arguments.audience,
961
+ role=arguments.role,
962
+ worker_policy_binding=_binding_from_environment(os.environ),
963
+ )
964
+ )
965
+ except HostedWorkerError as error:
966
+ print(f"{error.code}: {error.detail}", file=sys.stderr)
967
+ return 1
968
+ return 0
969
+
970
+
971
+ if __name__ == "__main__":
972
+ raise SystemExit(main())