gea-program 0.62.0__tar.gz → 0.63.0__tar.gz

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 (373) hide show
  1. {gea_program-0.62.0 → gea_program-0.63.0}/PKG-INFO +11 -1
  2. {gea_program-0.62.0 → gea_program-0.63.0}/README.md +10 -0
  3. {gea_program-0.62.0 → gea_program-0.63.0}/gea/__init__.py +1 -1
  4. {gea_program-0.62.0 → gea_program-0.63.0}/gea/__main__.py +43 -2
  5. {gea_program-0.62.0 → gea_program-0.63.0}/gea/acceptance_tests.py +366 -1
  6. {gea_program-0.62.0 → gea_program-0.63.0}/gea/casing_design.py +5 -0
  7. {gea_program-0.62.0 → gea_program-0.63.0}/gea/dossier.py +165 -4
  8. {gea_program-0.62.0 → gea_program-0.63.0}/gea/dossier_verify.py +41 -7
  9. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/IT_SECURITY.md +1 -0
  10. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/dossier.md +3 -1
  11. {gea_program-0.62.0 → gea_program-0.63.0}/gea/helplib.py +1 -1
  12. {gea_program-0.62.0 → gea_program-0.63.0}/gea/service.py +17 -0
  13. gea_program-0.63.0/gea/timestamp.py +568 -0
  14. {gea_program-0.62.0 → gea_program-0.63.0}/gea/web/app.html +13 -2
  15. {gea_program-0.62.0 → gea_program-0.63.0}/gea_program.egg-info/PKG-INFO +11 -1
  16. {gea_program-0.62.0 → gea_program-0.63.0}/gea_program.egg-info/SOURCES.txt +1 -0
  17. {gea_program-0.62.0 → gea_program-0.63.0}/pyproject.toml +1 -1
  18. {gea_program-0.62.0 → gea_program-0.63.0}/LICENSE +0 -0
  19. {gea_program-0.62.0 → gea_program-0.63.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  20. {gea_program-0.62.0 → gea_program-0.63.0}/gea/accuracy_statement.py +0 -0
  21. {gea_program-0.62.0 → gea_program-0.63.0}/gea/alarm_engine.py +0 -0
  22. {gea_program-0.62.0 → gea_program-0.63.0}/gea/alerts.py +0 -0
  23. {gea_program-0.62.0 → gea_program-0.63.0}/gea/backup.py +0 -0
  24. {gea_program-0.62.0 → gea_program-0.63.0}/gea/bench.py +0 -0
  25. {gea_program-0.62.0 → gea_program-0.63.0}/gea/blind_harness.py +0 -0
  26. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  27. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  28. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  29. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  30. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  31. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  32. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  33. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  34. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  35. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  36. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  37. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  38. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  39. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  40. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  41. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  42. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  43. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  44. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  45. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  46. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  47. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  48. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  49. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  50. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  51. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  52. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  53. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  54. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  55. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  56. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  57. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  58. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  59. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  60. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  61. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  62. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  63. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  64. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  65. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  66. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  67. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  68. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  69. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  70. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  71. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  72. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  73. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  74. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  75. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  76. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  77. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  78. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  79. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  80. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  81. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  82. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  83. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  84. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  85. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  86. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  87. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  88. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  89. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  90. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  91. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  92. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  93. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/med_160_sapropels.txt +0 -0
  94. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  95. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  96. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  97. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  98. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  99. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  100. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  101. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  102. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  103. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  104. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  105. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  106. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  107. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  108. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  109. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  110. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  111. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  112. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  113. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  114. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  115. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  116. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  117. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  118. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  119. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  120. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  121. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  122. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  123. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  124. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  125. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  126. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  127. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  128. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  129. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  130. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog_map.py +0 -0
  131. {gea_program-0.62.0 → gea_program-0.63.0}/gea/catalog_register.py +0 -0
  132. {gea_program-0.62.0 → gea_program-0.63.0}/gea/certificates.py +0 -0
  133. {gea_program-0.62.0 → gea_program-0.63.0}/gea/cli.py +0 -0
  134. {gea_program-0.62.0 → gea_program-0.63.0}/gea/client_reports.py +0 -0
  135. {gea_program-0.62.0 → gea_program-0.63.0}/gea/collision.py +0 -0
  136. {gea_program-0.62.0 → gea_program-0.63.0}/gea/config_versioning.py +0 -0
  137. {gea_program-0.62.0 → gea_program-0.63.0}/gea/conformance.py +0 -0
  138. {gea_program-0.62.0 → gea_program-0.63.0}/gea/control_room.py +0 -0
  139. {gea_program-0.62.0 → gea_program-0.63.0}/gea/correlation.py +0 -0
  140. {gea_program-0.62.0 → gea_program-0.63.0}/gea/dashboard.py +0 -0
  141. {gea_program-0.62.0 → gea_program-0.63.0}/gea/declarations.py +0 -0
  142. {gea_program-0.62.0 → gea_program-0.63.0}/gea/decline.py +0 -0
  143. {gea_program-0.62.0 → gea_program-0.63.0}/gea/deviation.py +0 -0
  144. {gea_program-0.62.0 → gea_program-0.63.0}/gea/dlis.py +0 -0
  145. {gea_program-0.62.0 → gea_program-0.63.0}/gea/dnp3.py +0 -0
  146. {gea_program-0.62.0 → gea_program-0.63.0}/gea/doctor.py +0 -0
  147. {gea_program-0.62.0 → gea_program-0.63.0}/gea/downhole_engine.py +0 -0
  148. {gea_program-0.62.0 → gea_program-0.63.0}/gea/drift_monitor.py +0 -0
  149. {gea_program-0.62.0 → gea_program-0.63.0}/gea/drill_string.py +0 -0
  150. {gea_program-0.62.0 → gea_program-0.63.0}/gea/earth_model.py +0 -0
  151. {gea_program-0.62.0 → gea_program-0.63.0}/gea/economics.py +0 -0
  152. {gea_program-0.62.0 → gea_program-0.63.0}/gea/ed25519.py +0 -0
  153. {gea_program-0.62.0 → gea_program-0.63.0}/gea/edr.py +0 -0
  154. {gea_program-0.62.0 → gea_program-0.63.0}/gea/elevation.py +0 -0
  155. {gea_program-0.62.0 → gea_program-0.63.0}/gea/etp.py +0 -0
  156. {gea_program-0.62.0 → gea_program-0.63.0}/gea/etp12_schemas.json +0 -0
  157. {gea_program-0.62.0 → gea_program-0.63.0}/gea/example_register_map.json +0 -0
  158. {gea_program-0.62.0 → gea_program-0.63.0}/gea/fat_sat.py +0 -0
  159. {gea_program-0.62.0 → gea_program-0.63.0}/gea/files.py +0 -0
  160. {gea_program-0.62.0 → gea_program-0.63.0}/gea/fixtures/README.md +0 -0
  161. {gea_program-0.62.0 → gea_program-0.63.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
  162. {gea_program-0.62.0 → gea_program-0.63.0}/gea/fixtures/comcat_mahoning_1990-2026.txt +0 -0
  163. {gea_program-0.62.0 → gea_program-0.63.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
  164. {gea_program-0.62.0 → gea_program-0.63.0}/gea/fixtures/ne_110m_coastline.json +0 -0
  165. {gea_program-0.62.0 → gea_program-0.63.0}/gea/follower.py +0 -0
  166. {gea_program-0.62.0 → gea_program-0.63.0}/gea/forward_model.py +0 -0
  167. {gea_program-0.62.0 → gea_program-0.63.0}/gea/frac_hits.py +0 -0
  168. {gea_program-0.62.0 → gea_program-0.63.0}/gea/friction_priors.py +0 -0
  169. {gea_program-0.62.0 → gea_program-0.63.0}/gea/gamma.py +0 -0
  170. {gea_program-0.62.0 → gea_program-0.63.0}/gea/gauge_aging.py +0 -0
  171. {gea_program-0.62.0 → gea_program-0.63.0}/gea/gauge_specs.py +0 -0
  172. {gea_program-0.62.0 → gea_program-0.63.0}/gea/geodesy.py +0 -0
  173. {gea_program-0.62.0 → gea_program-0.63.0}/gea/geomag.py +0 -0
  174. {gea_program-0.62.0 → gea_program-0.63.0}/gea/gravity_reference.py +0 -0
  175. {gea_program-0.62.0 → gea_program-0.63.0}/gea/hartip.py +0 -0
  176. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/TESTER_GUIDE.md +0 -0
  177. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/alarms.md +0 -0
  178. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/alerts.md +0 -0
  179. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/audit-update.md +0 -0
  180. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/backup.md +0 -0
  181. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/casing.md +0 -0
  182. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/catalog.md +0 -0
  183. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/charts.md +0 -0
  184. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/collision.md +0 -0
  185. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/company-sign-in.md +0 -0
  186. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/conformance.md +0 -0
  187. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/control-room.md +0 -0
  188. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/data-in.md +0 -0
  189. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/declare.md +0 -0
  190. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/decline.md +0 -0
  191. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/dlis.md +0 -0
  192. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/doctor.md +0 -0
  193. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/drift.md +0 -0
  194. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/drillstring.md +0 -0
  195. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/economics.md +0 -0
  196. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/edr.md +0 -0
  197. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/etp.md +0 -0
  198. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/field-ports.md +0 -0
  199. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/files.md +0 -0
  200. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/frac-hits.md +0 -0
  201. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/historian.md +0 -0
  202. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/https.md +0 -0
  203. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/hydraulics.md +0 -0
  204. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/instruments.md +0 -0
  205. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/interval.md +0 -0
  206. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/invoice-check.md +0 -0
  207. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/map.md +0 -0
  208. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/month-end.md +0 -0
  209. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/notifications.md +0 -0
  210. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/opseis.md +0 -0
  211. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/osdu.md +0 -0
  212. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/pad-summary.md +0 -0
  213. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/patches.md +0 -0
  214. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/penetrator.md +0 -0
  215. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/plan.md +0 -0
  216. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/pore-pressure.md +0 -0
  217. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/ppdm.md +0 -0
  218. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/quakeml.md +0 -0
  219. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/quality.md +0 -0
  220. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/report-history.md +0 -0
  221. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/rig-time.md +0 -0
  222. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/seedlink.md +0 -0
  223. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/seismic.md +0 -0
  224. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/setup.md +0 -0
  225. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/simulation.md +0 -0
  226. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/site.md +0 -0
  227. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/sites.md +0 -0
  228. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/slip.md +0 -0
  229. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/soak.md +0 -0
  230. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/sra.md +0 -0
  231. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/start.md +0 -0
  232. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/structure.md +0 -0
  233. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/tools.md +0 -0
  234. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/torque-drag.md +0 -0
  235. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/transients.md +0 -0
  236. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/upkeep.md +0 -0
  237. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/vibration.md +0 -0
  238. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/well-report.md +0 -0
  239. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/well-tests.md +0 -0
  240. {gea_program-0.62.0 → gea_program-0.63.0}/gea/help/well3d.md +0 -0
  241. {gea_program-0.62.0 → gea_program-0.63.0}/gea/housekeeping.py +0 -0
  242. {gea_program-0.62.0 → gea_program-0.63.0}/gea/hydraulics.py +0 -0
  243. {gea_program-0.62.0 → gea_program-0.63.0}/gea/interval.py +0 -0
  244. {gea_program-0.62.0 → gea_program-0.63.0}/gea/inverse_engine.py +0 -0
  245. {gea_program-0.62.0 → gea_program-0.63.0}/gea/invoice_check.py +0 -0
  246. {gea_program-0.62.0 → gea_program-0.63.0}/gea/jobs.py +0 -0
  247. {gea_program-0.62.0 → gea_program-0.63.0}/gea/live_ports.py +0 -0
  248. {gea_program-0.62.0 → gea_program-0.63.0}/gea/loadtest.py +0 -0
  249. {gea_program-0.62.0 → gea_program-0.63.0}/gea/matplotlib_demo.py +0 -0
  250. {gea_program-0.62.0 → gea_program-0.63.0}/gea/modbus.py +0 -0
  251. {gea_program-0.62.0 → gea_program-0.63.0}/gea/modbus_rtu.py +0 -0
  252. {gea_program-0.62.0 → gea_program-0.63.0}/gea/model_card.py +0 -0
  253. {gea_program-0.62.0 → gea_program-0.63.0}/gea/month_end.py +0 -0
  254. {gea_program-0.62.0 → gea_program-0.63.0}/gea/mqtt_port.py +0 -0
  255. {gea_program-0.62.0 → gea_program-0.63.0}/gea/notify.py +0 -0
  256. {gea_program-0.62.0 → gea_program-0.63.0}/gea/oidc.py +0 -0
  257. {gea_program-0.62.0 → gea_program-0.63.0}/gea/oidc_mock.py +0 -0
  258. {gea_program-0.62.0 → gea_program-0.63.0}/gea/op_seismicity.py +0 -0
  259. {gea_program-0.62.0 → gea_program-0.63.0}/gea/opcua_port.py +0 -0
  260. {gea_program-0.62.0 → gea_program-0.63.0}/gea/operator_app.py +0 -0
  261. {gea_program-0.62.0 → gea_program-0.63.0}/gea/osdu.py +0 -0
  262. {gea_program-0.62.0 → gea_program-0.63.0}/gea/pad_summary.py +0 -0
  263. {gea_program-0.62.0 → gea_program-0.63.0}/gea/patches.py +0 -0
  264. {gea_program-0.62.0 → gea_program-0.63.0}/gea/pdf_report.py +0 -0
  265. {gea_program-0.62.0 → gea_program-0.63.0}/gea/penetrator.py +0 -0
  266. {gea_program-0.62.0 → gea_program-0.63.0}/gea/permits.py +0 -0
  267. {gea_program-0.62.0 → gea_program-0.63.0}/gea/piwebapi.py +0 -0
  268. {gea_program-0.62.0 → gea_program-0.63.0}/gea/pore_pressure.py +0 -0
  269. {gea_program-0.62.0 → gea_program-0.63.0}/gea/ports.py +0 -0
  270. {gea_program-0.62.0 → gea_program-0.63.0}/gea/ppdm.py +0 -0
  271. {gea_program-0.62.0 → gea_program-0.63.0}/gea/profile_catalog.py +0 -0
  272. {gea_program-0.62.0 → gea_program-0.63.0}/gea/project.py +0 -0
  273. {gea_program-0.62.0 → gea_program-0.63.0}/gea/qt6_downhole_app.py +0 -0
  274. {gea_program-0.62.0 → gea_program-0.63.0}/gea/quakeml.py +0 -0
  275. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reconciler.py +0 -0
  276. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  277. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  278. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/WMM2025.COF +0 -0
  279. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
  280. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/eia_prices.provenance.json +0 -0
  281. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
  282. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/iscwsa_rev4_example.json +0 -0
  283. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/iscwsa_rev5_example.json +0 -0
  284. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/l0701_sonic_gr.las.gz +0 -0
  285. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/l0701_sonic_gr.provenance.json +0 -0
  286. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  287. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  288. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  289. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  290. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/mahoning_example.json +0 -0
  291. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/mud_motor_specs.json +0 -0
  292. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/owsg_tool_models.json +0 -0
  293. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/penetrator_ratings.json +0 -0
  294. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/seismicity_protocols.json +0 -0
  295. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/tool_sources.json +0 -0
  296. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/us_outline.json +0 -0
  297. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/us_outline.provenance.json +0 -0
  298. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/volve_3d.provenance.json +0 -0
  299. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
  300. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
  301. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
  302. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/volve_surfaces.json.gz +0 -0
  303. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
  304. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
  305. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
  306. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
  307. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
  308. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
  309. {gea_program-0.62.0 → gea_program-0.63.0}/gea/reference/volve_well_picks.csv.gz +0 -0
  310. {gea_program-0.62.0 → gea_program-0.63.0}/gea/report_history.py +0 -0
  311. {gea_program-0.62.0 → gea_program-0.63.0}/gea/rig_state.py +0 -0
  312. {gea_program-0.62.0 → gea_program-0.63.0}/gea/rock_inventory.py +0 -0
  313. {gea_program-0.62.0 → gea_program-0.63.0}/gea/sample_record.py +0 -0
  314. {gea_program-0.62.0 → gea_program-0.63.0}/gea/sample_well_profile.csv +0 -0
  315. {gea_program-0.62.0 → gea_program-0.63.0}/gea/sbom.py +0 -0
  316. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seedlink.py +0 -0
  317. {gea_program-0.62.0 → gea_program-0.63.0}/gea/segy.py +0 -0
  318. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seismic.py +0 -0
  319. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seismic_array.py +0 -0
  320. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seismic_assoc.py +0 -0
  321. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seismic_detect.py +0 -0
  322. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seismic_film.py +0 -0
  323. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seismic_harmonic.py +0 -0
  324. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seismic_qc.py +0 -0
  325. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seismic_response.py +0 -0
  326. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seismic_signature.py +0 -0
  327. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seismic_track.py +0 -0
  328. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seismic_uncertainty.py +0 -0
  329. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seismic_unlisted.py +0 -0
  330. {gea_program-0.62.0 → gea_program-0.63.0}/gea/seismic_watch.py +0 -0
  331. {gea_program-0.62.0 → gea_program-0.63.0}/gea/sensor_swap.py +0 -0
  332. {gea_program-0.62.0 → gea_program-0.63.0}/gea/series_cache.py +0 -0
  333. {gea_program-0.62.0 → gea_program-0.63.0}/gea/service_life.py +0 -0
  334. {gea_program-0.62.0 → gea_program-0.63.0}/gea/setup_checklist.py +0 -0
  335. {gea_program-0.62.0 → gea_program-0.63.0}/gea/shapefile.py +0 -0
  336. {gea_program-0.62.0 → gea_program-0.63.0}/gea/shell.py +0 -0
  337. {gea_program-0.62.0 → gea_program-0.63.0}/gea/shut_in.py +0 -0
  338. {gea_program-0.62.0 → gea_program-0.63.0}/gea/simulation.py +0 -0
  339. {gea_program-0.62.0 → gea_program-0.63.0}/gea/site_map.py +0 -0
  340. {gea_program-0.62.0 → gea_program-0.63.0}/gea/sla_report.py +0 -0
  341. {gea_program-0.62.0 → gea_program-0.63.0}/gea/slip.py +0 -0
  342. {gea_program-0.62.0 → gea_program-0.63.0}/gea/soak.py +0 -0
  343. {gea_program-0.62.0 → gea_program-0.63.0}/gea/sra.py +0 -0
  344. {gea_program-0.62.0 → gea_program-0.63.0}/gea/store_forward.py +0 -0
  345. {gea_program-0.62.0 → gea_program-0.63.0}/gea/strata_join.py +0 -0
  346. {gea_program-0.62.0 → gea_program-0.63.0}/gea/structure.py +0 -0
  347. {gea_program-0.62.0 → gea_program-0.63.0}/gea/supervisor.py +0 -0
  348. {gea_program-0.62.0 → gea_program-0.63.0}/gea/survey_cmd.py +0 -0
  349. {gea_program-0.62.0 → gea_program-0.63.0}/gea/survey_view.py +0 -0
  350. {gea_program-0.62.0 → gea_program-0.63.0}/gea/telemetry.py +0 -0
  351. {gea_program-0.62.0 → gea_program-0.63.0}/gea/tls.py +0 -0
  352. {gea_program-0.62.0 → gea_program-0.63.0}/gea/tool_catalog.py +0 -0
  353. {gea_program-0.62.0 → gea_program-0.63.0}/gea/tool_library.py +0 -0
  354. {gea_program-0.62.0 → gea_program-0.63.0}/gea/tool_taxonomy.py +0 -0
  355. {gea_program-0.62.0 → gea_program-0.63.0}/gea/torque_drag.py +0 -0
  356. {gea_program-0.62.0 → gea_program-0.63.0}/gea/transient.py +0 -0
  357. {gea_program-0.62.0 → gea_program-0.63.0}/gea/vibration.py +0 -0
  358. {gea_program-0.62.0 → gea_program-0.63.0}/gea/well3d.py +0 -0
  359. {gea_program-0.62.0 → gea_program-0.63.0}/gea/well_assembler.py +0 -0
  360. {gea_program-0.62.0 → gea_program-0.63.0}/gea/well_report.py +0 -0
  361. {gea_program-0.62.0 → gea_program-0.63.0}/gea/well_test_validation.py +0 -0
  362. {gea_program-0.62.0 → gea_program-0.63.0}/gea/wellplan.py +0 -0
  363. {gea_program-0.62.0 → gea_program-0.63.0}/gea/wellpos.py +0 -0
  364. {gea_program-0.62.0 → gea_program-0.63.0}/gea/wits0.py +0 -0
  365. {gea_program-0.62.0 → gea_program-0.63.0}/gea/witsml.py +0 -0
  366. {gea_program-0.62.0 → gea_program-0.63.0}/gea/workspace.py +0 -0
  367. {gea_program-0.62.0 → gea_program-0.63.0}/gea/xlsx_read.py +0 -0
  368. {gea_program-0.62.0 → gea_program-0.63.0}/gea_program.egg-info/dependency_links.txt +0 -0
  369. {gea_program-0.62.0 → gea_program-0.63.0}/gea_program.egg-info/entry_points.txt +0 -0
  370. {gea_program-0.62.0 → gea_program-0.63.0}/gea_program.egg-info/requires.txt +0 -0
  371. {gea_program-0.62.0 → gea_program-0.63.0}/gea_program.egg-info/top_level.txt +0 -0
  372. {gea_program-0.62.0 → gea_program-0.63.0}/setup.cfg +0 -0
  373. {gea_program-0.62.0 → gea_program-0.63.0}/tests/test_acceptance.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gea-program
3
- Version: 0.62.0
3
+ Version: 0.63.0
4
4
  Summary: GEA-Program: downhole gauge monitoring - canonical measurement records, quality rules, drift evaluation with a re-fit change log, well-test validation, alarm management, model cards, store-and-forward, monthly SLA measurement, FAT/SAT protocols and a dashboard, generated from live objects in the client's reporting outline.
5
5
  Author: Daniel T. Murphy
6
6
  License: MPL-2.0
@@ -683,6 +683,16 @@ catalogue well is added only from an entry that carries a downhole gauge
683
683
  series, with a well inside it and its gauge depth; a live seismic station
684
684
  whose feed has written nothing yet is listed as having no recording yet.
685
685
 
686
+ ### Trusted timestamps
687
+
688
+ A signed dossier's time is the signing machine's clock. With a public
689
+ time-stamping service named (RFC 3161: DigiCert, Sectigo or the site's
690
+ own), each dossier also carries the authority's signed word that its
691
+ signature existed at a given time - only the signature's SHA-256 is
692
+ sent - and the dossier's own checker verifies it to a root the reader's
693
+ computer trusts. `gea dossier --workspace C:\site --timestamp-url
694
+ http://timestamp.digicert.com --timestamp on`; `gea help dossier`.
695
+
686
696
  ### Casing design and kick tolerance
687
697
 
688
698
  Each casing string's burst, collapse and tension ratings by API TR 5C3,
@@ -644,6 +644,16 @@ catalogue well is added only from an entry that carries a downhole gauge
644
644
  series, with a well inside it and its gauge depth; a live seismic station
645
645
  whose feed has written nothing yet is listed as having no recording yet.
646
646
 
647
+ ### Trusted timestamps
648
+
649
+ A signed dossier's time is the signing machine's clock. With a public
650
+ time-stamping service named (RFC 3161: DigiCert, Sectigo or the site's
651
+ own), each dossier also carries the authority's signed word that its
652
+ signature existed at a given time - only the signature's SHA-256 is
653
+ sent - and the dossier's own checker verifies it to a root the reader's
654
+ computer trusts. `gea dossier --workspace C:\site --timestamp-url
655
+ http://timestamp.digicert.com --timestamp on`; `gea help dossier`.
656
+
647
657
  ### Casing design and kick tolerance
648
658
 
649
659
  Each casing string's burst, collapse and tension ratings by API TR 5C3,
@@ -123,7 +123,7 @@ from .profile_catalog import (
123
123
  read_pangaea_txt,
124
124
  )
125
125
 
126
- __version__ = "0.62.0"
126
+ __version__ = "0.63.0"
127
127
  __all__ = [
128
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
129
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -596,6 +596,12 @@ def main(argv=None) -> int:
596
596
  p_dz.add_argument("--rotate-key", action="store_true", help="retire this site's signing key and start a new one, signed over by the old (give --reason)")
597
597
  p_dz.add_argument("--reason", type=str, default="", help="with --rotate-key: why the key is retired")
598
598
  p_dz.add_argument("--key-history", action="store_true", help="the site's keys, current and retired, and the rotations between them")
599
+ p_dz.add_argument("--timestamp-url", type=str, default=None, help="the RFC 3161 time-stamping service to use, e.g. http://timestamp.digicert.com ('none' to forget it)")
600
+ p_dz.add_argument("--timestamp", type=str, choices=["on", "off"], default=None, help="stamp every dossier as it is signed (on) or not (off)")
601
+ p_dz.add_argument("--timestamp-anchors", type=str, default=None, help="a PEM file with the service's root certificate, when this machine's store does not hold it ('none' to forget it)")
602
+ p_dz.add_argument("--timestamp-test", action="store_true", help="ask the service to stamp a random hash and check its reply (nothing is written)")
603
+ p_dz.add_argument("--stamp", type=str, default=None, help="stamp a dossier this site signed, by its id (gea dossier --list)")
604
+ p_dz.add_argument("--anchors", type=str, default="", help="with --verify: a PEM file of roots to trust for the timestamp, beside this machine's own")
599
605
  p_dz.add_argument("--actor", type=str, default="cli")
600
606
  p_dz.add_argument("--json", action="store_true")
601
607
 
@@ -2507,11 +2513,40 @@ def main(argv=None) -> int:
2507
2513
  except WorkspaceError as e:
2508
2514
  raise SystemExit(f"dossier: {e}")
2509
2515
  if a.verify:
2510
- r = _DZ.verify(a.verify, ws, a.sources)
2516
+ if a.anchors and ws is None:
2517
+ from . import dossier_verify as _DV, timestamp as _TSM
2518
+ with open(a.anchors, "rb") as fh_:
2519
+ r = _DV.check(a.verify, a.sources, _TSM.ts_system_anchors() + _TSM.ts_pem_certs(fh_.read()))
2520
+ else:
2521
+ r = _DZ.verify(a.verify, ws, a.sources)
2511
2522
  print(_json.dumps(r, default=str) if a.json else _DZ.text(r))
2512
2523
  return 0 if r["status"] == "VALID" else 1
2513
2524
  if ws is None:
2514
2525
  raise SystemExit("dossier: give --workspace (or --verify FILE)")
2526
+ if a.timestamp_url is not None or a.timestamp is not None or a.timestamp_anchors is not None:
2527
+ try:
2528
+ st_ = _DZ.set_timestamp(ws, a.actor, ("" if (a.timestamp_url or "").lower() == "none" else a.timestamp_url),
2529
+ None if a.timestamp is None else a.timestamp == "on",
2530
+ ("" if (a.timestamp_anchors or "").lower() == "none" else a.timestamp_anchors))
2531
+ except ValueError as ex:
2532
+ raise SystemExit(f"dossier: {ex}")
2533
+ print(f"time-stamping service: {st_['url'] or 'none'}; every dossier stamped as it is signed: {'yes' if st_['enabled'] else 'no'}"
2534
+ + (f"; extra trusted roots: {st_['anchors']}" if st_["anchors"] else ""))
2535
+ return 0
2536
+ if a.timestamp_test or a.stamp:
2537
+ from . import timestamp as _TSM
2538
+ try:
2539
+ if a.timestamp_test:
2540
+ t_ = _DZ.test_timestamp(ws)
2541
+ print(f"{t_['url']}: {t_['status']} - {t_['tsa']} stamped {t_['gen_time']}" + (f" (accuracy {t_['accuracy_s']:g} s)" if t_.get("accuracy_s") else ""))
2542
+ print(f" the chain: {t_['chain_detail']}")
2543
+ return 0 if t_["status"] in ("TRUSTED", "VERIFIED") else 1
2544
+ rec = _DZ.stamp(ws, a.actor, a.stamp)
2545
+ except (KeyError, ValueError, OSError) as ex:
2546
+ raise SystemExit(f"dossier: {ex.args[0] if ex.args else ex}")
2547
+ print(f"stamped {rec['dossier_id']}: {rec['status']} - {rec['tsa']} at {rec['gen_time']} (signed {rec['signed_utc']} by this machine's clock)")
2548
+ print(f" the chain: {rec['chain']}")
2549
+ return 0
2515
2550
  if a.key:
2516
2551
  k = _DZ.key_info(ws)
2517
2552
  print(f"Ed25519 {k['fingerprint']} (public key {k['public_key']}, made {k['created_utc']})" if k else "no signing key yet: the first dossier makes one")
@@ -2534,8 +2569,11 @@ def main(argv=None) -> int:
2534
2569
  print("no key history yet")
2535
2570
  return 0
2536
2571
  if a.list:
2572
+ st_ = _DZ.stamps(ws)
2537
2573
  for e in reversed(_DZ.ledger(ws)):
2538
- print(f"{e['utc']} {e['dossier_id']} {e['display']:<24} {e['n_files']:>4} files {e['path']}")
2574
+ t_ = st_.get(e["dossier_id"]) or {}
2575
+ print(f"{e['utc']} {e['dossier_id']} {e['display']:<24} {e['n_files']:>4} files {e['path']}"
2576
+ + (f" [timestamp {t_['status']}" + (f" {t_['gen_time']}]" if t_.get("gen_time") else "]") if t_ else ""))
2539
2577
  return 0
2540
2578
  try:
2541
2579
  e = _DZ.build(ws, a.actor, __version__, a.well, a.include_sources)
@@ -2544,6 +2582,9 @@ def main(argv=None) -> int:
2544
2582
  print(f"signed {e['dossier_id']}: {e['display']}, {e['n_files']} file(s), {e['n_sources']} source record(s) {'embedded' if e['sources_embedded'] else 'by hash'}")
2545
2583
  print(f" {os.path.join(ws.reports_dir, e['path'])}")
2546
2584
  print(f" key fingerprint {e['fingerprint']}")
2585
+ t_ = e.get("timestamp")
2586
+ if t_:
2587
+ print(f" trusted timestamp: {t_['status']}" + (f" - {t_['tsa']} at {t_['gen_time']}" if t_.get("gen_time") else f" - {t_.get('reason', '')}"))
2547
2588
  return 0
2548
2589
  elif a.cmd == "collision":
2549
2590
  import json as _json
@@ -13611,6 +13611,11 @@ def section_de_real_gas(tmp: str) -> None:
13611
13611
  "carried": abs(k_["volume_shoe_as_bottom_bbl"] - carried) < 0.15 and "real-gas law" in k_["carried_by"] and "Boyle" in kf["carried_by"],
13612
13612
  "z_t": k_["z_bottom"] > 0 and abs(k_["temp_bottom_f"] - CSG.temperature_f(ex["config"], 10000.0 - h_b / 2)) < 0.1,
13613
13613
  "swabbed": all(k["swabbed"]["kick_ppg"] == k["mw_ppg"] and k["swabbed"]["volume_bbl"] > k["volume_bbl"] for k in ex["kicks"]),
13614
+ "open_hole": (lambda k: k.get("limited_by_open_hole") and k["governs"] == "the open hole itself" and "taller than the" in k["detail"])(
13615
+ CSG.analyse({**CSG.DEFAULTS, "strings": CSG.check_strings([{"what": "intermediate", "od_in": "9-5/8", "weight_lbft": 40, "grade": "N80", "top_md_ft": 0,
13616
+ "shoe_md_ft": 8000, "hole_in": 12.25, "mw_ppg": 9.5}]),
13617
+ "td_md_ft": 9000.0, "td_hole_in": 8.5, "td_mw_ppg": 10.0, "bha_od_in": 6.75, "bha_length_ft": 400.0, "dp_od_in": 5.0},
13618
+ lambda md: float(md), {"pp_const": 9.5, "fg_const": 14.0})["kicks"][0]),
13614
13619
  "burst_real": abs(sv["rows"][0]["burst_net_psi"] - top_load) <= 1.0 and sv["rows"][0]["burst_limited_by_shoe"]
13615
13620
  and "real gas" in sv["gas"] and "0.1 psi/ft" in exf["strings"][0]["gas"],
13616
13621
  }
@@ -13618,7 +13623,8 @@ def section_de_real_gas(tmp: str) -> None:
13618
13623
  ok(not failed3,
13619
13624
  "DE3 on the worked example: the gas's gradient in the bubble at its own middle's pressure and temperature, heavier on bottom than at the shoe; "
13620
13625
  "the bubble's heights from them; the shoe's volume carried to the bottom by p V / z T (Boyle's law with a fixed gradient); a swabbed kick "
13621
- "(no underbalance) beside each, larger; the burst load at the top the real gas's column from the fracture pressure at the shoe"
13626
+ "(no underbalance) beside each, larger; a shoe that would hold more gas than the open hole below it says the open hole is the limit; the burst load at "
13627
+ "the top the real gas's column from the fracture pressure at the shoe"
13622
13628
  + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
13623
13629
 
13624
13630
  # -- DE4 declared --------------------------------------------------------------------------------------------------------------
@@ -13663,6 +13669,364 @@ def section_de_real_gas(tmp: str) -> None:
13663
13669
  "the help" + (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
13664
13670
 
13665
13671
 
13672
+ # -- trusted timestamps: four time-stamp replies OpenSSL 3.0 made (a root, then RSA, ECDSA P-256, P-384 tokens over
13673
+ # SHA-256('hello dossier')), for section DF; made for these checks, by no real authority
13674
+ _TSF_CA = ('MIIDJDCCAgygAwIBAgIUGHYUXAMX2bEUAqBmXzWi3yefo28wDQYJKoZIhvcNAQELBQAwKjEVMBMGA1UEAwwMVGVzdCBSb290IENBMREwDwYDVQQKDAhHRUEg'
13675
+ 'dGVzdDAeFw0yNjEwMjIxMDEyNTVaFw0zNjEwMTkxMDEyNTVaMCoxFTATBgNVBAMMDFRlc3QgUm9vdCBDQTERMA8GA1UECgwIR0VBIHRlc3QwggEiMA0GCSqG'
13676
+ 'SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAgRnGIUewLTPEkHlyaS3hrMtqJ+nR2UiQGDXVZsbTyx0jw5yT/DbX95mxfQ0wGI4hA+QQwfycNUGyQ4PHOz133wjK'
13677
+ 'uH2PqyTxIWq10eSmk1bKN7xkrWn+M/530uPna5XcqGdd4fvYw3c2C4pns5ABjBpVS22CQ+UobLAP6XOBvdXskRDtBIuDE4kfUd7Z0BKLLVFLrFmhhimewBMW'
13678
+ 'K5FjMFQZL2GkI3WsTQpK9IZ84yOO8mVcVlBkQoR6U3YVY7M57ISO54ph1umAUkUK2jk8OctJW/2FgAQ3c+gJ94/yRQGgI9zCWzDBCfBUWjzwz1s6Q6dSfQx7'
13679
+ 'U7mQiYl4rOLpAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRrdCnx+4GIdaiSiwJ59XMT33qZKjANBgkqhkiG'
13680
+ '9w0BAQsFAAOCAQEAIWRaLc8LYGaOzxh8KPHOG6GNH7aTmBAvdPZDOyqyD7VL9psblX1Ad+yh68G4/fhcLhgy6SNHmcDG7bwa1n3qV1MnoOQm4qxsez/kujQC'
13681
+ '5KizMnJZjZqJY5PVgo+1/4hRvdP9WA8dv8TIw4/r4X9Q6H0ec74qCX2hZUrTeUjd1gibZ1RKgMP+kwQ0DEQOEdJU3eA5GRT/xpI6i5kFPpgb0HkkDQivYrqa'
13682
+ 'WKkANb6GAXMnDZbjGJWSQx0KwQ64h63itQ38CcVrIEZufwIWWgo9d41eDwRtvef0+SsyGE5c+iZOj6XBJRUGbDEMRfSxnPnXz55UU3N3ZUS00RruGnypEA==')
13683
+ _TSF_RSA = ('MIIJrzADAgEAMIIJpgYJKoZIhvcNAQcCoIIJlzCCCZMCAQMxDzANBglghkgBZQMEAgEFADCBqwYLKoZIhvcNAQkQAQSggZsEgZgwgZUCAQEGBCoDBAEwMTAN'
13684
+ 'BglghkgBZQMEAgEFAAQgpf3YGM3Xqxbz6uMRumW8TAcySa/vTTVJKIwpdN5IqogCAQIYDzIwMjYxMDIyMTAxMjU1WjAKAgEBgAIB9IEBZAEB/wIIM2L78IUF'
13685
+ 'H+igKqQoMCYxETAPBgNVBAMMCFRlc3QgVFNBMREwDwYDVQQKDAhHRUEgdGVzdKCCBrAwggNbMIICQ6ADAgECAhRQBQHe0ZbZiT6oIDo1qnmSZKb54TANBgkq'
13686
+ 'hkiG9w0BAQsFADAvMRowGAYDVQQDDBFUZXN0IEludGVybWVkaWF0ZTERMA8GA1UECgwIR0VBIHRlc3QwHhcNMjYxMDIyMTAxMjU1WhcNMzIwNDEzMTAxMjU1'
13687
+ 'WjAmMREwDwYDVQQDDAhUZXN0IFRTQTERMA8GA1UECgwIR0VBIHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEaAjK5vfOj1M9nOacc9gT'
13688
+ 'eQOghj1nOO6K7r01de2lphAJKC62ygn1rnYDpQFAvG9HZRtu6HngX6hj0t+ak5VNaOFi6E7o/+DXDkyAACu2rPRkvUp57OoQoqzZj5xbCHrBSq40M1s55/O9'
13689
+ 'nGQEn7k+iumhj+hrj85s3wGyJp0eqp/zepkzxuaqEnpm9vhJhX113YxOOOIe6mZaGGK4hmQ7QhBaPuS6YaPcfgJxcNL/YMjdNFzlBcL1FM1cIM3ytQfchw+1'
13690
+ '+I6pxqr867ZzfUI/13wfQNkl61c0KwX+zXp8eaEvuEabgbqtpkgla+qP4F7jbXJn0Pe4BuQBKukgSv1XAgMBAAGjeDB2MAwGA1UdEwEB/wQCMAAwDgYDVR0P'
13691
+ 'AQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMB0GA1UdDgQWBBR9pWr/h3aAurbsvhR8Oye4WYs2iDAfBgNVHSMEGDAWgBTnbKtOJfPeQuyAk/ny'
13692
+ '7nlUQ2X4UjANBgkqhkiG9w0BAQsFAAOCAQEABrxESQrdMoLXAPVXZETDKNqdxbPwgbaatIb/+uQ8bzz6W6Q/G39/8dYjYPmKVPW9euw66moX2QS2RPjg4boq'
13693
+ '1i/lrsWiBoVPPtzzRsKNv2t5q5dSvAgYAZSqFyyqXZFt+vJai8YH304ewa7q402ldLyYBHLJYTc7lqeTzXQSxUpjo3xctlaYkwOWAwYlDBVlQnoRR0Ix4hRx'
13694
+ 'rOnbzqHqF8KFEYp9BqIZB8VpsZF7UhbCtz95a8CUD3RXh4V47LwW0y5Bb/2s8PxQcZ09Z20yDBDYGwLw+nXuhwu3x3UAQ9TtsoT4KGknDNSKLREcmw8Jczfw'
13695
+ 'Rbu+0nofDuO4FyPsaDCCA00wggI1oAMCAQICFDrWSB8IC6uckCbmVUN+44yj+yaIMA0GCSqGSIb3DQEBCwUAMCoxFTATBgNVBAMMDFRlc3QgUm9vdCBDQTER'
13696
+ 'MA8GA1UECgwIR0VBIHRlc3QwHhcNMjYxMDIyMTAxMjU1WhcNMzUwMTA4MTAxMjU1WjAvMRowGAYDVQQDDBFUZXN0IEludGVybWVkaWF0ZTERMA8GA1UECgwI'
13697
+ 'R0VBIHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrB+E7+70zmq2oxTxsOFzvPMPJ2KSsvntmm9ULxKWynAQpQDsqtl8iQGpC7Tshfkkg'
13698
+ 'dWy6UocKCc7lFe+JJ3PYpA0dC0gpLt4gjlxOV1qroLit5jy3hdea89dLGgZYCHvgkTtOct1VKIVqfpHLbzZaedyXlzlT4wJS+qSwETebyRPoW49ilOwZt2tR'
13699
+ 'McDOA+gmXQhwHwcvzPoBExLGqocAqupbYcZ6kuL4WX1/IofuMHjjmXxJwa5PyTmL0xIaXp8jHpbn6doLbf71McQo7Mqzy7MUjvmr/xmJvuGlWtuZjK2269d4'
13700
+ 'OGnjv7H3942Q/DS9uPP0kC2B/72iudtNbwE3AgMBAAGjZjBkMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTnbKtOJfPe'
13701
+ 'QuyAk/ny7nlUQ2X4UjAfBgNVHSMEGDAWgBRrdCnx+4GIdaiSiwJ59XMT33qZKjANBgkqhkiG9w0BAQsFAAOCAQEAtKynEqrAob2dE01ik6ZmKjHg2ejV2Di+'
13702
+ 'nFSWMlUrmpFvvVu2Jiolgy/53gdisyg01j55n6fgCP8jKpcJtKznBPOWFST7dmxzBUe/ds4IMVrNtpzow/6IdwU8iiRdungQ26kJHbKaKXuUykgIv3KjsfrK'
13703
+ '3apBjBoj8wgg5bt42Q/s5KQpuq3aw146eCkrJfF1rXBl3ekBovzSjlWmC++Zof37qB+WSuDq87m/6KaBSr3TA0DQgX2gRv7/brhaZbPMZIWW7bWoRLFfYQaw'
13704
+ 'yKyubuZKDS5Sl4x0dlDxdvVr8hTV+YUC8Z3DrCHySSE1qPDOKo6CY7eYXoQSuwUukJnFpDGCAhkwggIVAgEBMEcwLzEaMBgGA1UEAwwRVGVzdCBJbnRlcm1l'
13705
+ 'ZGlhdGUxETAPBgNVBAoMCEdFQSB0ZXN0AhRQBQHe0ZbZiT6oIDo1qnmSZKb54TANBglghkgBZQMEAgEFAKCBpDAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQ'
13706
+ 'AQQwHAYJKoZIhvcNAQkFMQ8XDTI2MTAyMjEwMTI1NVowLwYJKoZIhvcNAQkEMSIEIPSAbRnRwUsYAuiCIZ3h+E5u+EceAdcrebQdBylF1HNnMDcGCyqGSIb3'
13707
+ 'DQEJEAIvMSgwJjAkMCIEINXcpmLFUV+aUxLz17QKKpkxQkav4w801wiJq0X0IO/eMA0GCSqGSIb3DQEBAQUABIIBAFPjwPVhgHEl6T+KMxJGgzOyU3GNeNqG'
13708
+ 'bpS32ebB+hlJ0/r6WUD86NpulH+qMEEuX1rNx21TElRViQ0dSqy9i82vgWz4FW9Owfz/ReNroBhoPUb0im8ETD6hj73E6Ol7kxpz6k6/LLGwevNU5VwY3LtB'
13709
+ 'UI7HHVj7DY2/7sjT6QyeFljjEgh8+eLFWUqvccFs0gDJxZG+S2E8Qy1Lmk3CqH0UqhH1XdfuYAd8wdmIbsdhpARI4vviXsA066cz/WDJxd8o04247hxuRa5h'
13710
+ '2JlKap5MuH8Sity0JYld2AGj1CRi9meUiRCXLcRqbf3PvN71wy9ZpsTvValRht9DQLdlVu8=')
13711
+ _TSF_P256 = ('MIIILTADAgEAMIIIJAYJKoZIhvcNAQcCoIIIFTCCCBECAQMxDzANBglghkgBZQMEAgEFADCBrgYLKoZIhvcNAQkQAQSggZ4EgZswgZgCAQEGBCoDBAEwMTAN'
13712
+ 'BglghkgBZQMEAgEFAAQgpf3YGM3Xqxbz6uMRumW8TAcySa/vTTVJKIwpdN5IqogCAQMYDzIwMjYxMDIyMTAxMjU1WjAKAgEBgAIB9IEBZAEB/wIIM2L78IUF'
13713
+ 'H+igLaQrMCkxFDASBgNVBAMMC1Rlc3QgVFNBIEVDMREwDwYDVQQKDAhHRUEgdGVzdKCCBegwggKTMIIBe6ADAgECAhRQBQHe0ZbZiT6oIDo1qnmSZKb54jAN'
13714
+ 'BgkqhkiG9w0BAQsFADAvMRowGAYDVQQDDBFUZXN0IEludGVybWVkaWF0ZTERMA8GA1UECgwIR0VBIHRlc3QwHhcNMjYxMDIyMTAxMjU1WhcNMzIwNDEzMTAx'
13715
+ 'MjU1WjApMRQwEgYDVQQDDAtUZXN0IFRTQSBFQzERMA8GA1UECgwIR0VBIHRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARYQt0GNa05PQcAimdBLWJr'
13716
+ 'o5drbydyOi2aDyOXJmLpq+z7kEflU8S1K6WEsBE6eb4V4+q/KdrGyE+Oiss0D+dio3gwdjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUB'
13717
+ 'Af8EDDAKBggrBgEFBQcDCDAdBgNVHQ4EFgQUs4jt9hGv65H2e00mgeUU8VuIRGgwHwYDVR0jBBgwFoAU52yrTiXz3kLsgJP58u55VENl+FIwDQYJKoZIhvcN'
13718
+ 'AQELBQADggEBADmps17KDCyCHwSsP6Pxix78qe9G+ZdSt/qEglOXZwuyHWRRO3C9FkcUx1vdB8R+YOdlE9qlItcZMXXWjGayw5YikJTHM7bJg8rz4mDg7XfR'
13719
+ 'sFF1xQVlVyCO68gaXXZ88PAbjQW7lMT6h8GuLcrpQJbyYfSiDpXuJdkpPD02pu5KvxLW3CZAUbxDQkZ9aAJc6Z34jCNEWyEqyQ920OTPuAgf4sllbWG2m5gy'
13720
+ '4FZvGrpVsFm3DmcktfGdIZ9P1OQ1g4+d2gh8SfcVtloPHXgwMq8t/TznJrKUCTzRiKLLUv9sIskMwfmHmKPtN3UzHvypT8dQwODkMNGF77EX+MjvNLEwggNN'
13721
+ 'MIICNaADAgECAhQ61kgfCAurnJAm5lVDfuOMo/smiDANBgkqhkiG9w0BAQsFADAqMRUwEwYDVQQDDAxUZXN0IFJvb3QgQ0ExETAPBgNVBAoMCEdFQSB0ZXN0'
13722
+ 'MB4XDTI2MTAyMjEwMTI1NVoXDTM1MDEwODEwMTI1NVowLzEaMBgGA1UEAwwRVGVzdCBJbnRlcm1lZGlhdGUxETAPBgNVBAoMCEdFQSB0ZXN0MIIBIjANBgkq'
13723
+ 'hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqwfhO/u9M5qtqMU8bDhc7zzDydikrL57ZpvVC8SlspwEKUA7KrZfIkBqQu07IX5JIHVsulKHCgnO5RXviSdz2KQN'
13724
+ 'HQtIKS7eII5cTldaq6C4reY8t4XXmvPXSxoGWAh74JE7TnLdVSiFan6Ry282Wnncl5c5U+MCUvqksBE3m8kT6FuPYpTsGbdrUTHAzgPoJl0IcB8HL8z6ARMS'
13725
+ 'xqqHAKrqW2HGepLi+Fl9fyKH7jB445l8ScGuT8k5i9MSGl6fIx6W5+naC23+9THEKOzKs8uzFI75q/8Zib7hpVrbmYyttuvXeDhp47+x9/eNkPw0vbjz9JAt'
13726
+ 'gf+9ornbTW8BNwIDAQABo2YwZDASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU52yrTiXz3kLsgJP58u55VENl+FIwHwYD'
13727
+ 'VR0jBBgwFoAUa3Qp8fuBiHWokosCefVzE996mSowDQYJKoZIhvcNAQELBQADggEBALSspxKqwKG9nRNNYpOmZiox4Nno1dg4vpxUljJVK5qRb71btiYqJYMv'
13728
+ '+d4HYrMoNNY+eZ+n4Aj/IyqXCbSs5wTzlhUk+3ZscwVHv3bOCDFazbac6MP+iHcFPIokXbp4ENupCR2ymil7lMpICL9yo7H6yt2qQYwaI/MIIOW7eNkP7OSk'
13729
+ 'Kbqt2sNeOngpKyXxda1wZd3pAaL80o5VpgvvmaH9+6gflkrg6vO5v+imgUq90wNA0IF9oEb+/264WmWzzGSFlu21qESxX2EGsMisrm7mSg0uUpeMdHZQ8Xb1'
13730
+ 'a/IU1fmFAvGdw6wh8kkhNajwziqOgmO3mF6EErsFLpCZxaQxggFcMIIBWAIBATBHMC8xGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRlMREwDwYDVQQKDAhH'
13731
+ 'RUEgdGVzdAIUUAUB3tGW2Yk+qCA6Nap5kmSm+eIwDQYJYIZIAWUDBAIBBQCggaQwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEP'
13732
+ 'Fw0yNjEwMjIxMDEyNTVaMC8GCSqGSIb3DQEJBDEiBCAE5TS0HR7scBy9+RNkbVuDYeCK19GuauwAzM4y8IftLDA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCDm'
13733
+ 'G6Gj1g2jPRj1bJc7UXIIPz6Rv/2DCLeU0atHHaV9FjAKBggqhkjOPQQDAgRIMEYCIQDYrOX3jrGqg6+DmhVBgNyEpDFGL+l+MVc76XfBs8Ip/AIhAM4HN/88'
13734
+ 'd+nd/I+vxtLUz5GF/V/ZAc0n0W3zdqHsAa7S')
13735
+ _TSF_P384 = ('MIIIfzADAgEAMIIIdgYJKoZIhvcNAQcCoIIIZzCCCGMCAQMxDzANBglghkgBZQMEAgIFADCBsQYLKoZIhvcNAQkQAQSggaEEgZ4wgZsCAQEGBCoDBAEwMTAN'
13736
+ 'BglghkgBZQMEAgEFAAQgpf3YGM3Xqxbz6uMRumW8TAcySa/vTTVJKIwpdN5IqogCAQQYDzIwMjYxMDIyMTAxMjU2WjAKAgEBgAIB9IEBZAEB/wIIM2L78IUF'
13737
+ 'H+igMKQuMCwxFzAVBgNVBAMMDlRlc3QgVFNBIEVDMzg0MREwDwYDVQQKDAhHRUEgdGVzdKCCBggwggKzMIIBm6ADAgECAhRQBQHe0ZbZiT6oIDo1qnmSZKb5'
13738
+ '4zANBgkqhkiG9w0BAQsFADAvMRowGAYDVQQDDBFUZXN0IEludGVybWVkaWF0ZTERMA8GA1UECgwIR0VBIHRlc3QwHhcNMjYxMDIyMTAxMjU1WhcNMzIwNDEz'
13739
+ 'MTAxMjU1WjAsMRcwFQYDVQQDDA5UZXN0IFRTQSBFQzM4NDERMA8GA1UECgwIR0VBIHRlc3QwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATyIqtdYgECcyF0LELj'
13740
+ 'MX2frh4Gs0ijfkRwAs11+0F7Gn4U89nI5evfWeE2o18RfDiBPShixZY6ja/R0EVZRbyjnNRZQgesqfUsXzymYMczOUxR62ceJBgfYgYseL2cglujeDB2MAwG'
13741
+ 'A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMB0GA1UdDgQWBBQqloFXH2ZIof62vG5VyBK/9KLtbDAfBgNVHSME'
13742
+ 'GDAWgBTnbKtOJfPeQuyAk/ny7nlUQ2X4UjANBgkqhkiG9w0BAQsFAAOCAQEAE+b5Cqjyup3JH6APIj33IRWaFimUM7K5tGHwXB0TpQsg6V8WUdniiXln/BiQ'
13743
+ 'soSF9RNoyCGx2JTaHyoSJcfd0ncY+AbwVkc3Jcv9p19Pkl8gSrdsTIfuXP3l1Ft+WKJyk86ACOVPRheH/sI964hrgNz885+0m2mUFsvybGVVVyMnfot5WC33'
13744
+ 'KvxcC/FQ3FPledyadoG+0t2566WLWXImBNJtnFrqdS6oUqP5foSh3b10GG5ToeoAiD0zcX+84YHyTMSOgu8MWYVzpoyEY7Korc73xfdPc7jprA2g3zsH4jKh'
13745
+ 't7Y4CjS/Cb7IVSlVv5cPrPwOF8OTPAMrIABHL41k5jCCA00wggI1oAMCAQICFDrWSB8IC6uckCbmVUN+44yj+yaIMA0GCSqGSIb3DQEBCwUAMCoxFTATBgNV'
13746
+ 'BAMMDFRlc3QgUm9vdCBDQTERMA8GA1UECgwIR0VBIHRlc3QwHhcNMjYxMDIyMTAxMjU1WhcNMzUwMTA4MTAxMjU1WjAvMRowGAYDVQQDDBFUZXN0IEludGVy'
13747
+ 'bWVkaWF0ZTERMA8GA1UECgwIR0VBIHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrB+E7+70zmq2oxTxsOFzvPMPJ2KSsvntmm9ULxKWy'
13748
+ 'nAQpQDsqtl8iQGpC7TshfkkgdWy6UocKCc7lFe+JJ3PYpA0dC0gpLt4gjlxOV1qroLit5jy3hdea89dLGgZYCHvgkTtOct1VKIVqfpHLbzZaedyXlzlT4wJS'
13749
+ '+qSwETebyRPoW49ilOwZt2tRMcDOA+gmXQhwHwcvzPoBExLGqocAqupbYcZ6kuL4WX1/IofuMHjjmXxJwa5PyTmL0xIaXp8jHpbn6doLbf71McQo7Mqzy7MU'
13750
+ 'jvmr/xmJvuGlWtuZjK2269d4OGnjv7H3942Q/DS9uPP0kC2B/72iudtNbwE3AgMBAAGjZjBkMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEG'
13751
+ 'MB0GA1UdDgQWBBTnbKtOJfPeQuyAk/ny7nlUQ2X4UjAfBgNVHSMEGDAWgBRrdCnx+4GIdaiSiwJ59XMT33qZKjANBgkqhkiG9w0BAQsFAAOCAQEAtKynEqrA'
13752
+ 'ob2dE01ik6ZmKjHg2ejV2Di+nFSWMlUrmpFvvVu2Jiolgy/53gdisyg01j55n6fgCP8jKpcJtKznBPOWFST7dmxzBUe/ds4IMVrNtpzow/6IdwU8iiRdungQ'
13753
+ '26kJHbKaKXuUykgIv3KjsfrK3apBjBoj8wgg5bt42Q/s5KQpuq3aw146eCkrJfF1rXBl3ekBovzSjlWmC++Zof37qB+WSuDq87m/6KaBSr3TA0DQgX2gRv7/'
13754
+ 'brhaZbPMZIWW7bWoRLFfYQawyKyubuZKDS5Sl4x0dlDxdvVr8hTV+YUC8Z3DrCHySSE1qPDOKo6CY7eYXoQSuwUukJnFpDGCAYswggGHAgEBMEcwLzEaMBgG'
13755
+ 'A1UEAwwRVGVzdCBJbnRlcm1lZGlhdGUxETAPBgNVBAoMCEdFQSB0ZXN0AhRQBQHe0ZbZiT6oIDo1qnmSZKb54zANBglghkgBZQMEAgIFAKCBtDAaBgkqhkiG'
13756
+ '9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI2MTAyMjEwMTI1NlowNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQg2CMpQc5q4duxT+WhU1zW'
13757
+ '2bOVC/86InR/3U3cQYMCJGAwPwYJKoZIhvcNAQkEMTIEMAUaSVBfI+TaGuBVL7kEknIuC8+Z6IgUpbcnd2Wgj2SO5gI9nalHk8y9ophHiOvDUDAKBggqhkjO'
13758
+ 'PQQDAwRnMGUCMFKzJVdh8wAgcksAjKzqiaDyUrGE8PouWM1EuBy86ZUiqIgcAXabuOT/cKGbz//kLgIxAMP9axYsDTCCkiJDlT7VNsTQuA+K1Df/n6Cu4/ph'
13759
+ 'MQsKFLjuOOhqJIoQHBXQkLfeAw==')
13760
+
13761
+
13762
+ def _test_tsa(bits: int = 2048, eku_critical: bool = True, wrong_nonce: bool = False, break_signature: bool = False,
13763
+ gen_time=None, ca_valid_days: int = 3650):
13764
+ """A time-stamping authority for the checks, never a real one: a root it made, a TSA certificate the root issued, and
13765
+ a function answering an RFC 3161 request as a service would (CMS SignedData, RSA with SHA-256, ESS v2)."""
13766
+ import hashlib as _h
13767
+ import secrets as _sec
13768
+ from datetime import datetime as _dt, timedelta as _td, timezone as _tz
13769
+ from . import timestamp as TS
13770
+ from . import tls as TL
13771
+ S, I, O, T_, N, E = TL._seq, TL._int, TL._oid, TL._tlv, TL._name, TL._ext
13772
+ rsa_alg = S(O('1.2.840.113549.1.1.11'), T_(0x05, b''))
13773
+ now = _dt.now(_tz.utc).replace(microsecond=0)
13774
+
13775
+ def spki(k):
13776
+ return S(S(O('1.2.840.113549.1.1.1'), T_(0x05, b'')), T_(0x03, b'\x00' + S(I(k['n']), I(k['e']))))
13777
+
13778
+ def cert(serial, subject, issuer, key, signer_key, exts, days):
13779
+ tbs = S(T_(0xA0, I(2)), I(serial), rsa_alg, issuer, S(TL._time(now - _td(days=1)), TL._time(now + _td(days=days))), subject, spki(key), T_(0xA3, S(*exts)))
13780
+ return S(tbs, rsa_alg, T_(0x03, b'\x00' + TL.sign_pkcs1_sha256(signer_key, tbs)))
13781
+ ca_key, tsa_key = TL.rsa_key(bits), TL.rsa_key(bits)
13782
+ ca_name, tsa_name = N('GEA Test Root (not a real authority)', 'GEA acceptance'), N('GEA Test TSA (not a real authority)', 'GEA acceptance')
13783
+ ca = cert(1001, ca_name, ca_name, ca_key, ca_key, [E('2.5.29.19', True, S(T_(0x01, b'\xff'))), E('2.5.29.15', True, T_(0x03, b'\x01\x06'))], ca_valid_days)
13784
+ tsa = cert(1002, tsa_name, ca_name, tsa_key, ca_key, [E('2.5.29.19', True, S()), E('2.5.29.15', True, T_(0x03, b'\x07\x80')),
13785
+ E('2.5.29.37', eku_critical, S(O('1.3.6.1.5.5.7.3.8')))], 1800)
13786
+ served = []
13787
+
13788
+ def answer(url, req, timeout=None):
13789
+ k = TS._ts_kids(TS._ts_one(req)[1])
13790
+ imp = k[1][2]
13791
+ nonce = next((TS._ts_int(v) for t, v, _ in k[2:] if t == 0x02), None)
13792
+ if wrong_nonce and nonce is not None:
13793
+ nonce += 1
13794
+ when = gen_time or _dt.now(_tz.utc)
13795
+ serial = _sec.randbits(62)
13796
+ tst = S(I(1), O('1.3.6.1.4.1.99999.1'), imp, I(serial), T_(0x18, when.strftime('%Y%m%d%H%M%SZ').encode('ascii')), S(I(1)),
13797
+ *([I(nonce)] if nonce is not None else []))
13798
+ attrs = sorted([S(O('1.2.840.113549.1.9.3'), TL._set(O('1.2.840.113549.1.9.16.1.4'))),
13799
+ S(O('1.2.840.113549.1.9.4'), TL._set(T_(0x04, _h.sha256(tst).digest()))),
13800
+ S(O('1.2.840.113549.1.9.16.2.47'), TL._set(S(S(S(T_(0x04, _h.sha256(tsa).digest()))))))])
13801
+ signed = TL._set(*attrs)
13802
+ sig = TL.sign_pkcs1_sha256(tsa_key, signed)
13803
+ if break_signature:
13804
+ sig = sig[:-1] + bytes([sig[-1] ^ 1])
13805
+ si = S(I(1), S(ca_name, I(1002)), S(O('2.16.840.1.101.3.4.2.1'), T_(0x05, b'')), b'\xa0' + signed[1:],
13806
+ S(O('1.2.840.113549.1.1.1'), T_(0x05, b'')), T_(0x04, sig))
13807
+ sd = S(I(3), TL._set(S(O('2.16.840.1.101.3.4.2.1'), T_(0x05, b''))), S(O('1.2.840.113549.1.9.16.1.4'), T_(0xA0, T_(0x04, tst))),
13808
+ T_(0xA0, tsa), TL._set(si))
13809
+ served.append(serial)
13810
+ return S(S(I(0)), S(O('1.2.840.113549.1.7.2'), T_(0xA0, sd)))
13811
+ return {'ca': ca, 'tsa': tsa, 'answer': answer, 'served': served}
13812
+
13813
+
13814
+ def section_df_trusted_timestamp(tmp: str) -> None:
13815
+ """Section DF - trusted timestamps (RFC 3161) on the signed dossier: tokens an independent implementation (OpenSSL
13816
+ 3.0) made with RSA, ECDSA P-256 and P-384 read and verified to their root, and every tampering caught; a request
13817
+ OpenSSL reads; a dossier stamped as it is signed through a service over HTTP, the token inside, the standalone
13818
+ checker reading it with and without the root trusted; a service that cannot be reached leaves the dossier signed
13819
+ and says so; stamping later; the settings, the command line, the API, the tab and the help."""
13820
+ import base64 as _b64
13821
+ import hashlib
13822
+ import http.cookiejar
13823
+ import http.server
13824
+ import subprocess as _sp
13825
+ import threading as _th
13826
+ import urllib.error
13827
+ import urllib.request
13828
+ import zipfile as _zf
13829
+ from datetime import datetime as _dt, timedelta as _td, timezone as _tz
13830
+ from . import __version__ as _ver
13831
+ from . import dossier as DZ
13832
+ from . import helplib as H
13833
+ from . import timestamp as TS
13834
+ from .service import Service, Users
13835
+ from .workspace import Workspace
13836
+ pkg = Path(__file__).parent
13837
+ fx = {k: _b64.b64decode(v) for k, v in (('ca', _TSF_CA), ('rsa', _TSF_RSA), ('p256', _TSF_P256), ('p384', _TSF_P384))}
13838
+ data_hash = hashlib.sha256(b'hello dossier').digest()
13839
+
13840
+ # -- DF1 tokens OpenSSL made ------------------------------------------------------------------------------------------------
13841
+ res = {k: TS.ts_verify(TS.ts_read_response(fx[k])['token'], data_hash, anchors=[fx['ca']]) for k in ('rsa', 'p256', 'p384')}
13842
+ tok = TS.ts_read_response(fx['rsa'])['token']
13843
+ other = TS.ts_verify(tok, hashlib.sha256(b'another dossier').digest(), anchors=[fx['ca']])
13844
+ untrusted = TS.ts_verify(tok, data_hash, anchors=[])
13845
+ i_t = tok.find(TS.ts_read_token(tok)['tst_der'])
13846
+ t_tst = TS.ts_verify(tok[:i_t + 40] + bytes([tok[i_t + 40] ^ 1]) + tok[i_t + 41:], data_hash, anchors=[fx['ca']])
13847
+ t_sig = TS.ts_verify(tok[:-3] + bytes([tok[-3] ^ 1]) + tok[-2:], data_hash, anchors=[fx['ca']])
13848
+ req, nonce = TS.ts_request(data_hash, nonce=12345)
13849
+ rk = TS._ts_kids(TS._ts_one(req)[1])
13850
+ try:
13851
+ TS.ts_request(b'short')
13852
+ short = ''
13853
+ except TS.TimestampError as e:
13854
+ short = str(e)
13855
+ c1 = {
13856
+ "openssl_tokens": all(r['status'] == 'TRUSTED' for r in res.values()) and res['rsa']['gen_time'] == '2026-10-22T10:12:55Z'
13857
+ and res['rsa']['tsa'] == 'Test TSA' and abs(res['rsa']['accuracy_s'] - 1.5001) < 1e-9,
13858
+ "algorithms": 'RSA with SHA256' in res['rsa']['checks'][3]['detail'] and 'ECDSA P-256 with SHA256' in res['p256']['checks'][3]['detail']
13859
+ and 'ECDSA P-384 with SHA384' in res['p384']['checks'][3]['detail'],
13860
+ "chain": res['rsa']['chain'] == ['Test TSA', 'Test Intermediate', 'Test Root CA'] and res['rsa']['root'] == 'Test Root CA',
13861
+ "other_hash": other['status'] == 'INVALID' and not other['checks'][1]['ok'],
13862
+ "not_trusted": untrusted['status'] == 'VERIFIED' and 'not trusted here' in untrusted['chain_detail'],
13863
+ "tampered": t_tst['status'] == 'INVALID' and t_sig['status'] == 'INVALID',
13864
+ "request": len(req) > 50 and TS._ts_int(rk[0][1]) == 1 and TS._ts_kids(rk[1][1])[1][1] == data_hash and TS._ts_int(rk[2][1]) == 12345
13865
+ and rk[3][1] == b'\xff' and 'a sha256 hash is 32 bytes' in short,
13866
+ }
13867
+ failed1 = [k for k, v in c1.items() if not v]
13868
+ ok(not failed1,
13869
+ "DF1 time-stamp tokens OpenSSL 3.0 made (RSA, ECDSA P-256, P-384) read and verified: the hash they cover, the signed attributes, the authority's "
13870
+ "signature, its signing-certificate attribute, a time-stamping certificate, the chain through the intermediate to the root; another hash, a "
13871
+ "changed TSTInfo or signature INVALID; the root not trusted here VERIFIED, said so; the request as RFC 3161 lays it out"
13872
+ + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
13873
+
13874
+ # -- DF2 a service made here, and what a bad one does -------------------------------------------------------------------------------
13875
+ good = _test_tsa()
13876
+ bad_nonce, soft_eku, bad_sig = _test_tsa(wrong_nonce=True), _test_tsa(eku_critical=False), _test_tsa(break_signature=True)
13877
+ late = _test_tsa(gen_time=_dt.now(_tz.utc) + _td(days=4000))
13878
+
13879
+ def ask(t):
13880
+ rq, n = TS.ts_request(data_hash)
13881
+ return TS.ts_verify(TS.ts_read_response(t['answer']('x', rq))['token'], data_hash, nonce=n, anchors=[t['ca']])
13882
+ rg, rn, re_, rs, rl = ask(good), ask(bad_nonce), ask(soft_eku), ask(bad_sig), ask(late)
13883
+ try: # a TimeStampResp whose status is 2 (rejection), "no"
13884
+ TS.ts_read_response(b'\x30\x0b\x30\x09\x02\x01\x02\x30\x04\x0c\x02no')
13885
+ refused_ = ''
13886
+ except TS.TimestampError as e:
13887
+ refused_ = str(e)
13888
+ c2 = {
13889
+ "good": rg['status'] == 'TRUSTED' and any(c['check'] == 'the nonce is the one sent' and c['ok'] for c in rg['checks']),
13890
+ "nonce": rn['status'] == 'INVALID' and any(c['check'] == 'the nonce is the one sent' and not c['ok'] for c in rn['checks']),
13891
+ "eku": re_['status'] == 'INVALID' and any('time stamping only' in c['detail'] for c in re_['checks'] if not c['ok']),
13892
+ "signature": rs['status'] == 'INVALID' and any(c['check'] == "the authority's signature verifies" and not c['ok'] for c in rs['checks']),
13893
+ "out_of_validity": rl['status'] == 'INVALID' and any('not valid at the time stamped' in c['detail'] for c in rl['checks']),
13894
+ "rejected": 'did not grant a time stamp: rejection' in refused_ and '(no)' in refused_,
13895
+ }
13896
+ failed2 = [k for k, v in c2.items() if not v]
13897
+ ok(not failed2,
13898
+ "DF2 a service made here with the program's own RSA (a root and a time-stamping certificate it issued) answers and verifies; a reply with "
13899
+ "another nonce, a certificate not marked for time stamping only, a broken signature, a time outside the certificate's validity are each "
13900
+ "INVALID, said which; a rejection is reported with the service's words" + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
13901
+
13902
+ # -- DF3 a dossier stamped as it is signed, over HTTP ---------------------------------------------------------------------------------
13903
+ d = Path(tmp, "tsa"); d.mkdir()
13904
+ (d / "root.pem").write_text('-----BEGIN CERTIFICATE-----\n' + _b64.encodebytes(good['ca']).decode() + '-----END CERTIFICATE-----\n', encoding='ascii')
13905
+
13906
+ class TSAHandler(http.server.BaseHTTPRequestHandler):
13907
+ def log_message(self, *a):
13908
+ pass
13909
+
13910
+ def do_POST(self):
13911
+ n = int(self.headers.get('Content-Length') or 0)
13912
+ body = good['answer']('x', self.rfile.read(n))
13913
+ self.send_response(200); self.send_header('Content-Type', 'application/timestamp-reply'); self.send_header('Content-Length', str(len(body)))
13914
+ self.end_headers(); self.wfile.write(body)
13915
+ srv = http.server.ThreadingHTTPServer(('127.0.0.1', 0), TSAHandler)
13916
+ _th.Thread(target=srv.serve_forever, daemon=True).start()
13917
+ url = f"http://127.0.0.1:{srv.server_address[1]}/tsr"
13918
+ wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
13919
+ ws.add_well_catalog("volve_f12_f14_production_excerpt", "15/9-F-12", 10000.0, actor="tester")
13920
+ ws.refresh_dashboard(actor="tester")
13921
+
13922
+ def refused(**kw):
13923
+ try:
13924
+ DZ.set_timestamp(ws, "tester", **kw)
13925
+ return ''
13926
+ except ValueError as e:
13927
+ return str(e)
13928
+ m_url, m_on, m_anc = refused(url='ftp://x'), refused(enabled=True), refused(anchors=str(d / 'none.pem'))
13929
+ st = DZ.set_timestamp(ws, "tester", url, True, str(d / "root.pem"))
13930
+ e1 = DZ.build(ws, "tester", _ver, None)
13931
+ zp = os.path.join(ws.reports_dir, e1['path'])
13932
+ with _zf.ZipFile(zp) as z_:
13933
+ names = set(z_.namelist())
13934
+ verifier = z_.read('verify_dossier.py')
13935
+ (d / "verify_dossier.py").write_bytes(verifier)
13936
+ v_any = _sp.run([sys.executable, str(d / "verify_dossier.py"), zp], capture_output=True, text=True, cwd=str(d))
13937
+ v_anc = _sp.run([sys.executable, str(d / "verify_dossier.py"), zp, "--anchors", str(d / "root.pem")], capture_output=True, text=True, cwd=str(d))
13938
+ v_ws = DZ.verify(zp, ws)
13939
+ with _zf.ZipFile(zp) as z_: # a token changed after the fact
13940
+ items = [(i, z_.read(i.filename)) for i in z_.infolist()]
13941
+ zp_bad = str(d / "bad.zip")
13942
+ with _zf.ZipFile(zp_bad, 'w') as z_:
13943
+ for i, b in items:
13944
+ z_.writestr(i, (b[:-5] + bytes([b[-5] ^ 1]) + b[-4:]) if i.filename == 'timestamp.tsr' else b)
13945
+ v_bad = _sp.run([sys.executable, str(d / "verify_dossier.py"), zp_bad, "--anchors", str(d / "root.pem")], capture_output=True, text=True, cwd=str(d))
13946
+ DZ.set_timestamp(ws, "tester", "http://127.0.0.1:9/tsr") # nothing listens there
13947
+ e2 = DZ.build(ws, "tester", _ver, None)
13948
+ DZ.set_timestamp(ws, "tester", url)
13949
+ later = DZ.stamp(ws, "tester", e2['dossier_id'])
13950
+ stamps = DZ.stamps(ws)
13951
+ pg = DZ.page(ws)
13952
+ audit = Path(ws.audit_path).read_text(encoding="utf-8")
13953
+ c3 = {
13954
+ "settings": "http:// or https://" in m_url and "address first" in m_on and "no file" in m_anc and st['enabled'] and st['url'] == url,
13955
+ "stamped": e1['timestamp']['status'] == 'TRUSTED' and 'timestamp.tsr' in names and e1['timestamp']['tsa'].startswith('GEA Test TSA'),
13956
+ "checker_anchor": v_anc.returncode == 0 and "VALID" in v_anc.stdout and "the trusted timestamp verifies" in v_anc.stdout
13957
+ and "a root this computer trusts" in v_anc.stdout and "after the signing machine's clock" in v_anc.stdout,
13958
+ "checker_untrusted": v_any.returncode == 0 and "does not reach a root this computer trusts" in v_any.stdout,
13959
+ "workspace": v_ws['status'] == 'VALID' and v_ws['timestamp']['status'] == 'TRUSTED',
13960
+ "tampered": v_bad.returncode == 1 and "INVALID" in v_bad.stdout and "NO the trusted timestamp verifies" in v_bad.stdout,
13961
+ "unreachable": e2['timestamp']['status'] == 'NOT STAMPED' and 'did not answer' in e2['timestamp']['reason'] and os.path.isfile(os.path.join(ws.reports_dir, e2['path'])),
13962
+ "later": later['status'] == 'TRUSTED' and stamps[e2['dossier_id']]['status'] == 'TRUSTED' and DZ.verify(os.path.join(ws.reports_dir, e2['path']), ws)['status'] == 'VALID',
13963
+ "page": pg['timestamp']['url'] == url and all(x['timestamp'] and x['timestamp']['status'] == 'TRUSTED' for x in pg['dossiers']),
13964
+ "audit": '"dossier.timestamp"' in audit and '"dossier.timestamp_settings"' in audit and 'NOT STAMPED' in audit,
13965
+ }
13966
+ failed3 = [k for k, v in c3.items() if not v]
13967
+ ok(not failed3,
13968
+ "DF3 a dossier stamped as it is signed, through a service over HTTP: the token rides in it; the standalone checker reads it - TRUSTED with the "
13969
+ "service's root given, its chain short of a trusted root said so without it, a changed token INVALID; a service that cannot be reached leaves "
13970
+ "the dossier signed and NOT STAMPED with why, and stamping it later adds the token; settings refused when wrong; audited"
13971
+ + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
13972
+
13973
+ # -- DF4 the command line, the API, the tab, the help ------------------------------------------------------------------------------
13974
+ run = lambda *a: _sp.run([sys.executable, "-m", "gea", "dossier", "--workspace", wsp, *a], capture_output=True, text=True, cwd=str(pkg.parent))
13975
+ r_test = run("--timestamp-test")
13976
+ e3 = DZ.build(ws, "tester", _ver, None)
13977
+ r_stamp = run("--stamp", e3['dossier_id'])
13978
+ r_list = run("--list")
13979
+ r_off = run("--timestamp", "off")
13980
+ r_none = run("--timestamp-url", "none")
13981
+ st_none = DZ.timestamp_settings(ws)
13982
+ DZ.set_timestamp(ws, "tester", url, True)
13983
+ Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator"); Users(str(Path(wsp, "users.json"))).add("adm", "admin-pass-123", "admin")
13984
+ svc = Service(wsp, port=0, scheduler=False).start()
13985
+ base_url = svc.url.rstrip("/")
13986
+
13987
+ def opener(name, pw_):
13988
+ o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
13989
+ rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
13990
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
13991
+ return o
13992
+
13993
+ def call(o, method, path, body=None):
13994
+ rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
13995
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
13996
+ try:
13997
+ with o.open(rq, timeout=120) as rr_:
13998
+ return rr_.status, json.loads(rr_.read())
13999
+ except urllib.error.HTTPError as e:
14000
+ return e.code, json.loads(e.read())
14001
+ try:
14002
+ op, adm = opener("op", "op-pass-123"), opener("adm", "admin-pass-123")
14003
+ s_op, _ = call(op, "POST", "/api/dossier/timestamp/settings", {"url": url})
14004
+ s_ad, sa = call(adm, "POST", "/api/dossier/timestamp/settings", {"url": url, "enabled": True})
14005
+ t_ad, ta = call(adm, "POST", "/api/dossier/timestamp/test", {})
14006
+ sg, sgv = call(op, "POST", "/api/dossier/sign", {})
14007
+ gp, gpv = call(op, "GET", "/api/dossier")
14008
+ st_x, sx = call(op, "POST", "/api/dossier/stamp", {"dossier_id": "nope"})
14009
+ finally:
14010
+ svc.stop(); srv.shutdown()
14011
+ page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
14012
+ it = H.security_text() or ""
14013
+ c4 = {
14014
+ "cli": r_test.returncode == 0 and "TRUSTED" in r_test.stdout and r_stamp.returncode == 0 and "stamped " in r_stamp.stdout
14015
+ and "[timestamp TRUSTED" in r_list.stdout and r_off.returncode == 0 and "stamped as it is signed: no" in r_off.stdout
14016
+ and r_none.returncode == 0 and st_none['url'] is None and not st_none['enabled'],
14017
+ "api": s_op == 403 and s_ad == 200 and sa['enabled'] and t_ad == 200 and ta['status'] == 'TRUSTED' and sg == 200 and sgv['timestamp']['status'] == 'TRUSTED'
14018
+ and gp == 200 and gpv['dossiers'][0]['timestamp']['status'] == 'TRUSTED' and st_x == 404,
14019
+ "page": all(x in page_src for x in ("/api/dossier/timestamp/settings", "/api/dossier/timestamp/test", "/api/dossier/stamp", "Trusted timestamp", "dz-stamp")),
14020
+ "help": not H.check("dossier") and "--timestamp-url http://timestamp.digicert.com" in (H.page("dossier") or "") and "Trusted timestamp" in it
14021
+ and "only the SHA-256 of a dossier's signature" in it,
14022
+ }
14023
+ failed4 = [k for k, v in c4.items() if not v]
14024
+ ok(not failed4,
14025
+ "DF4 from the command line (the service set, tested, a dossier stamped, the list showing it, turned off, forgotten), the API (the settings for "
14026
+ "the administrator only, the test, signing stamped), the Reports tab, the help and the IT overview naming the service and what is sent"
14027
+ + (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
14028
+
14029
+
13666
14030
  def PR_read_ok(path) -> bool:
13667
14031
  from . import pdf_report as PR
13668
14032
  try:
@@ -13783,6 +14147,7 @@ def main() -> int:
13783
14147
  section_dc_every_tab(tmp)
13784
14148
  section_dd_casing_design(tmp)
13785
14149
  section_de_real_gas(tmp)
14150
+ section_df_trusted_timestamp(tmp)
13786
14151
  if _FAILS:
13787
14152
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
13788
14153
  for f in _FAILS:
@@ -620,6 +620,11 @@ def kick_tolerance(cfg: dict, i: int, tvd_of, win) -> Optional[dict]:
620
620
  out.update({'volume_at_bottom_bbl': round(v_bottom, 1), 'volume_at_shoe_bbl': round(v_shoe, 1), 'volume_shoe_as_bottom_bbl': round(v_shoe_at_bottom, 1),
621
621
  'volume_bbl': round(min(v_bottom, v_shoe_at_bottom), 1), 'carried_by': law,
622
622
  'governs': 'the bubble at the bottom' if v_bottom <= v_shoe_at_bottom else 'the bubble at the shoe'})
623
+ if bot['h'] > open_h and sho['h'] > open_h:
624
+ out['governs'] = 'the open hole itself'
625
+ out['limited_by_open_hole'] = True
626
+ out['detail'] = (f"the shoe would hold a gas column taller than the {open_h:,.0f} ft of open hole below it: the tolerance is the open hole's "
627
+ "whole volume, and the shoe is not what limits it")
623
628
  return out
624
629
 
625
630