zog 0.1.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 (414) hide show
  1. zog/__init__.py +3 -0
  2. zog/_release_manifest.json +142 -0
  3. zog/archive_mirror/__init__.py +3 -0
  4. zog/archive_mirror/__main__.py +2 -0
  5. zog/archive_mirror/apps.py +6 -0
  6. zog/archive_mirror/cli.py +91 -0
  7. zog/archive_mirror/collector.py +70 -0
  8. zog/archive_mirror/config.py +22 -0
  9. zog/archive_mirror/lease.py +22 -0
  10. zog/archive_mirror/management/__init__.py +0 -0
  11. zog/archive_mirror/management/commands/__init__.py +0 -0
  12. zog/archive_mirror/migrations/0001_initial.py +55 -0
  13. zog/archive_mirror/migrations/0002_collectionstatus.py +22 -0
  14. zog/archive_mirror/migrations/0003_reviewed_sources.py +33 -0
  15. zog/archive_mirror/migrations/__init__.py +0 -0
  16. zog/archive_mirror/models.py +51 -0
  17. zog/archive_mirror/notice_contract.py +200 -0
  18. zog/archive_mirror/notices.py +84 -0
  19. zog/archive_mirror/observation.py +166 -0
  20. zog/archive_mirror/preflight.py +52 -0
  21. zog/archive_mirror/receipt_notices.py +131 -0
  22. zog/archive_mirror/runtime.py +58 -0
  23. zog/archive_mirror/settings.py +14 -0
  24. zog/archive_mirror/source_inputs.py +173 -0
  25. zog/archive_mirror/source_notices.py +79 -0
  26. zog/archive_mirror/store.py +101 -0
  27. zog/archive_mirror/urls.py +10 -0
  28. zog/archive_mirror/views.py +156 -0
  29. zog/box_control/__init__.py +25 -0
  30. zog/box_control/__main__.py +2 -0
  31. zog/box_control/api.py +552 -0
  32. zog/box_control/application_storage.py +286 -0
  33. zog/box_control/boot.py +19 -0
  34. zog/box_control/build_jobs.py +297 -0
  35. zog/box_control/build_protocol.py +75 -0
  36. zog/box_control/cancellation.py +102 -0
  37. zog/box_control/cli.py +7 -0
  38. zog/box_control/dependency.py +139 -0
  39. zog/box_control/diagnostics.py +43 -0
  40. zog/box_control/durability.py +103 -0
  41. zog/box_control/errors.py +44 -0
  42. zog/box_control/events.py +70 -0
  43. zog/box_control/explanations.py +97 -0
  44. zog/box_control/generation.py +122 -0
  45. zog/box_control/host_gateway.py +51 -0
  46. zog/box_control/images.py +66 -0
  47. zog/box_control/inspection.py +245 -0
  48. zog/box_control/locking.py +58 -0
  49. zog/box_control/model.py +118 -0
  50. zog/box_control/mounts.py +92 -0
  51. zog/box_control/operations.py +515 -0
  52. zog/box_control/preflight.py +111 -0
  53. zog/box_control/project.py +76 -0
  54. zog/box_control/reconcile.py +681 -0
  55. zog/box_control/requests.py +199 -0
  56. zog/box_control/retention.py +135 -0
  57. zog/box_control/root_protocol.py +34 -0
  58. zog/box_control/runtime/__init__.py +35 -0
  59. zog/box_control/runtime/reference.py +367 -0
  60. zog/box_control/runtime/root_control.py +188 -0
  61. zog/box_control/runtime/systemd.py +859 -0
  62. zog/box_control/specification.py +336 -0
  63. zog/box_control/state.py +30 -0
  64. zog/box_control/station.py +70 -0
  65. zog/box_control/workspaces.py +251 -0
  66. zog/build_record/__init__.py +29 -0
  67. zog/build_record/__main__.py +3 -0
  68. zog/build_record/audit.py +62 -0
  69. zog/build_record/cli.py +68 -0
  70. zog/build_record/graph.py +448 -0
  71. zog/build_record/image_build.py +701 -0
  72. zog/build_record/model.py +578 -0
  73. zog/build_record/snapshot.py +25 -0
  74. zog/build_record/store.py +70 -0
  75. zog/build_trace/__init__.py +8 -0
  76. zog/build_trace/__main__.py +2 -0
  77. zog/build_trace/api.py +515 -0
  78. zog/build_trace/cli.py +208 -0
  79. zog/build_trace/generations.py +256 -0
  80. zog/build_trace/history.py +268 -0
  81. zog/build_trace/integration.py +100 -0
  82. zog/build_trace/materials.py +122 -0
  83. zog/build_trace/model.py +106 -0
  84. zog/build_trace/observations.py +409 -0
  85. zog/build_trace/provenance.py +165 -0
  86. zog/build_trace/shell.py +50 -0
  87. zog/build_trace/snapshot_observations.py +642 -0
  88. zog/build_trace/source.py +166 -0
  89. zog/build_trace/summaries.py +96 -0
  90. zog/host_deploy/__init__.py +3 -0
  91. zog/host_deploy/artifacts.py +141 -0
  92. zog/host_deploy/beacon_install.py +190 -0
  93. zog/host_deploy/beacon_installation/requirements-security-tested.txt +28 -0
  94. zog/host_deploy/beacon_payload.py +45 -0
  95. zog/host_deploy/beacon_sources.json +71 -0
  96. zog/host_deploy/bootstrap.py +59 -0
  97. zog/host_deploy/bulk.py +184 -0
  98. zog/host_deploy/checkpoint.py +142 -0
  99. zog/host_deploy/cli.py +206 -0
  100. zog/host_deploy/diagnostics.py +25 -0
  101. zog/host_deploy/discovery.py +112 -0
  102. zog/host_deploy/gateway/__init__.py +27 -0
  103. zog/host_deploy/gateway/github_dispatch.py +254 -0
  104. zog/host_deploy/gateway/protocol.py +384 -0
  105. zog/host_deploy/image_build_lanes.py +380 -0
  106. zog/host_deploy/job_worker.py +135 -0
  107. zog/host_deploy/jobs.py +228 -0
  108. zog/host_deploy/provision.py +238 -0
  109. zog/host_deploy/reboot_remote.py +266 -0
  110. zog/host_deploy/reboots.py +170 -0
  111. zog/host_deploy/remote.py +62 -0
  112. zog/host_deploy/remote_control.py +61 -0
  113. zog/host_deploy/runner.py +206 -0
  114. zog/host_deploy/station.py +65 -0
  115. zog/host_deploy/station_remote.py +193 -0
  116. zog/host_deploy/transfer.py +57 -0
  117. zog/host_deploy/workspace.py +63 -0
  118. zog/host_discover/__init__.py +1 -0
  119. zog/host_discover/__main__.py +2 -0
  120. zog/host_discover/admission.py +114 -0
  121. zog/host_discover/beacon.py +184 -0
  122. zog/host_discover/beacon_state.py +36 -0
  123. zog/host_discover/bootstrap.py +39 -0
  124. zog/host_discover/box_bridge.py +92 -0
  125. zog/host_discover/daemon.py +241 -0
  126. zog/host_discover/destinations.py +65 -0
  127. zog/host_discover/gateway.py +39 -0
  128. zog/host_discover/managed.py +156 -0
  129. zog/host_discover/managed_transport.py +173 -0
  130. zog/host_discover/recovery.py +220 -0
  131. zog/host_discover/retry.py +31 -0
  132. zog/host_discover/roles.py +116 -0
  133. zog/host_discover/supervisor.py +276 -0
  134. zog/host_discover/systemd/host-discover-beacon.service +22 -0
  135. zog/host_discover/systemd/host-discover-managed.service +23 -0
  136. zog/host_discover/systemd/host-discover-supervisor.socket +22 -0
  137. zog/host_discover/systemd/host-discover-supervisor@.service +18 -0
  138. zog/host_discover/systemd/host-discover.service +21 -0
  139. zog/host_identify/__init__.py +3 -0
  140. zog/host_identify/archive.py +56 -0
  141. zog/host_identify/initialization.py +211 -0
  142. zog/host_identify/managed.py +57 -0
  143. zog/host_identify/mirror_access.py +38 -0
  144. zog/host_identify/mirror_fixture.py +22 -0
  145. zog/host_identify/recovery.py +34 -0
  146. zog/host_identify/signatures.py +88 -0
  147. zog/host_identify/storage.py +168 -0
  148. zog/host_install/__init__.py +7 -0
  149. zog/host_install/__main__.py +3 -0
  150. zog/host_install/artifact.py +189 -0
  151. zog/host_install/beacon_profile.py +126 -0
  152. zog/host_install/cli.py +136 -0
  153. zog/host_install/filesystem.py +72 -0
  154. zog/host_install/identity_worker.py +46 -0
  155. zog/host_install/image.py +220 -0
  156. zog/host_install/layout.py +66 -0
  157. zog/host_install/manifest.py +118 -0
  158. zog/host_install/preflight.py +111 -0
  159. zog/host_install/state_admission.py +207 -0
  160. zog/host_install/state_contract.py +191 -0
  161. zog/host_install/state_gate.py +55 -0
  162. zog/host_install/state_initialize.py +222 -0
  163. zog/host_install/state_inspect.py +253 -0
  164. zog/host_install/state_io.py +128 -0
  165. zog/host_install/state_probe.py +66 -0
  166. zog/host_install/state_provision.py +196 -0
  167. zog/host_install/systemd/host-discover-managed.conf.example +18 -0
  168. zog/host_install/systemd/host-install-admission.socket +23 -0
  169. zog/host_install/systemd/host-install-admission@.service +20 -0
  170. zog/host_install/systemd/host-install-identity.service +16 -0
  171. zog/host_install/systemd/host-install-prepare.service +16 -0
  172. zog/host_install/systemd/state.mount.in +14 -0
  173. zog/image_build/__init__.py +26 -0
  174. zog/image_build/__main__.py +81 -0
  175. zog/image_build/application_software.py +684 -0
  176. zog/image_build/application_views.py +822 -0
  177. zog/image_build/archive_mirror.py +250 -0
  178. zog/image_build/artifact_observations.py +76 -0
  179. zog/image_build/artifacts.py +113 -0
  180. zog/image_build/automake_results.py +39 -0
  181. zog/image_build/box_control_adapter.py +219 -0
  182. zog/image_build/build_environment.py +45 -0
  183. zog/image_build/build_views.py +39 -0
  184. zog/image_build/cleanup.py +129 -0
  185. zog/image_build/compiler_environment.py +62 -0
  186. zog/image_build/compiler_results.py +34 -0
  187. zog/image_build/configuration.py +57 -0
  188. zog/image_build/developer/__init__.py +1 -0
  189. zog/image_build/developer/catalogue.py +106 -0
  190. zog/image_build/developer/distribution.py +129 -0
  191. zog/image_build/developer/host.py +178 -0
  192. zog/image_build/developer/seed.py +170 -0
  193. zog/image_build/developer/seed_build.py +188 -0
  194. zog/image_build/developer/source_packaging.py +71 -0
  195. zog/image_build/engine.py +653 -0
  196. zog/image_build/errors.py +2 -0
  197. zog/image_build/filesystem.py +163 -0
  198. zog/image_build/final_compiler.py +228 -0
  199. zog/image_build/final_math.py +66 -0
  200. zog/image_build/generation_provenance.py +249 -0
  201. zog/image_build/glibc_final.py +251 -0
  202. zog/image_build/glibc_publish.py +71 -0
  203. zog/image_build/glibc_tests.py +41 -0
  204. zog/image_build/host_export.py +298 -0
  205. zog/image_build/host_fixture.py +69 -0
  206. zog/image_build/host_inventory.py +51 -0
  207. zog/image_build/host_labels.py +83 -0
  208. zog/image_build/host_trust.py +91 -0
  209. zog/image_build/host_trust_assembly.py +109 -0
  210. zog/image_build/info_index.py +63 -0
  211. zog/image_build/installed_verification.py +121 -0
  212. zog/image_build/integration_observations.py +180 -0
  213. zog/image_build/licensing.py +354 -0
  214. zog/image_build/local_network.py +32 -0
  215. zog/image_build/metadata.py +246 -0
  216. zog/image_build/native.py +135 -0
  217. zog/image_build/native_stage.py +70 -0
  218. zog/image_build/native_tools.py +156 -0
  219. zog/image_build/observation_snapshots.py +462 -0
  220. zog/image_build/pins.py +60 -0
  221. zog/image_build/pipeline.py +207 -0
  222. zog/image_build/provenance.py +430 -0
  223. zog/image_build/public_https.py +131 -0
  224. zog/image_build/python_enrollment.py +167 -0
  225. zog/image_build/python_runtime.py +166 -0
  226. zog/image_build/remote_operations.py +1324 -0
  227. zog/image_build/retained_compiler.py +157 -0
  228. zog/image_build/runner.py +106 -0
  229. zog/image_build/source_acquisition.py +65 -0
  230. zog/image_build/source_libraries.py +103 -0
  231. zog/image_build/source_provenance.py +88 -0
  232. zog/image_build/sources.py +114 -0
  233. zog/image_build/stages.py +225 -0
  234. zog/image_build/temporary.py +114 -0
  235. zog/image_build/test_fixtures.py +90 -0
  236. zog/image_build/trace_records.py +147 -0
  237. zog/image_build/verification_observations.py +112 -0
  238. zog/image_build/worker.py +29 -0
  239. zog/integrate_observe/__init__.py +6 -0
  240. zog/integrate_observe/__main__.py +3 -0
  241. zog/integrate_observe/cli.py +77 -0
  242. zog/integrate_observe/observer.py +386 -0
  243. zog/network_register/__init__.py +3 -0
  244. zog/network_register/__main__.py +2 -0
  245. zog/network_register/adoption.py +78 -0
  246. zog/network_register/cli.py +57 -0
  247. zog/network_register/client.py +94 -0
  248. zog/network_register/cloudflare.py +155 -0
  249. zog/network_register/contracts.py +109 -0
  250. zog/network_register/dns.py +91 -0
  251. zog/network_register/dns_authority.py +190 -0
  252. zog/network_register/engine.py +272 -0
  253. zog/network_register/evidence.py +171 -0
  254. zog/network_register/host_demo.py +51 -0
  255. zog/network_register/initialization.py +97 -0
  256. zog/network_register/ledger.py +253 -0
  257. zog/network_register/lifecycle.py +145 -0
  258. zog/network_register/models.py +170 -0
  259. zog/network_register/operations.py +115 -0
  260. zog/network_register/porkbun.py +117 -0
  261. zog/network_register/porkbun_dns.py +181 -0
  262. zog/network_register/reservations.py +79 -0
  263. zog/network_register/state.py +42 -0
  264. zog/network_register/store.py +88 -0
  265. zog/root_control/__init__.py +3 -0
  266. zog/root_control/__main__.py +3 -0
  267. zog/root_control/build.py +519 -0
  268. zog/root_control/daemon.py +193 -0
  269. zog/root_control/events.py +11 -0
  270. zog/root_control/jobs.py +159 -0
  271. zog/root_control/journal.py +136 -0
  272. zog/root_control/journal_barrier.py +223 -0
  273. zog/root_control/native/journal-wait.c +29 -0
  274. zog/root_control/privileged.py +28 -0
  275. zog/root_control/processes.py +162 -0
  276. zog/root_control/protocol.py +30 -0
  277. zog/root_control/systemd.py +562 -0
  278. zog/root_control/workspace.py +160 -0
  279. zog/station_access/__init__.py +3 -0
  280. zog/station_access/admin.py +68 -0
  281. zog/station_access/api/__init__.py +0 -0
  282. zog/station_access/api/serializers.py +65 -0
  283. zog/station_access/api/urls.py +31 -0
  284. zog/station_access/api/views.py +652 -0
  285. zog/station_access/apps.py +9 -0
  286. zog/station_access/archive_inventory/__init__.py +0 -0
  287. zog/station_access/archive_inventory/apps.py +6 -0
  288. zog/station_access/archive_inventory/contract.py +100 -0
  289. zog/station_access/archive_inventory/generation_resolution.py +51 -0
  290. zog/station_access/archive_inventory/generation_views.py +283 -0
  291. zog/station_access/archive_inventory/management/__init__.py +0 -0
  292. zog/station_access/archive_inventory/management/commands/__init__.py +0 -0
  293. zog/station_access/archive_inventory/management/commands/report_archive_inventory.py +103 -0
  294. zog/station_access/archive_inventory/migrations/0001_initial.py +57 -0
  295. zog/station_access/archive_inventory/migrations/0002_noticebundle.py +13 -0
  296. zog/station_access/archive_inventory/migrations/__init__.py +0 -0
  297. zog/station_access/archive_inventory/models.py +46 -0
  298. zog/station_access/archive_inventory/notice_views.py +107 -0
  299. zog/station_access/archive_inventory/urls.py +12 -0
  300. zog/station_access/archive_inventory/views.py +187 -0
  301. zog/station_access/box_control/__init__.py +11 -0
  302. zog/station_access/box_control/current.py +247 -0
  303. zog/station_access/box_control/fixture.py +120 -0
  304. zog/station_access/box_control/journal.py +76 -0
  305. zog/station_access/box_control/logs.py +47 -0
  306. zog/station_access/box_control/port.py +52 -0
  307. zog/station_access/box_control/types.py +44 -0
  308. zog/station_access/box_control/unavailable.py +7 -0
  309. zog/station_access/cli.py +82 -0
  310. zog/station_access/host_registry/__init__.py +0 -0
  311. zog/station_access/host_registry/admin.py +9 -0
  312. zog/station_access/host_registry/apps.py +6 -0
  313. zog/station_access/host_registry/cloud_configuration.py +140 -0
  314. zog/station_access/host_registry/deployment_jobs.py +195 -0
  315. zog/station_access/host_registry/deployment_views.py +44 -0
  316. zog/station_access/host_registry/dns_cutover.py +210 -0
  317. zog/station_access/host_registry/dns_destination_views.py +37 -0
  318. zog/station_access/host_registry/dns_destinations.py +222 -0
  319. zog/station_access/host_registry/dns_evidence.py +162 -0
  320. zog/station_access/host_registry/dns_membership.py +165 -0
  321. zog/station_access/host_registry/dns_membership_views.py +28 -0
  322. zog/station_access/host_registry/dns_reconcile.py +221 -0
  323. zog/station_access/host_registry/dns_setup.py +194 -0
  324. zog/station_access/host_registry/dns_setup_views.py +25 -0
  325. zog/station_access/host_registry/dns_shared.py +140 -0
  326. zog/station_access/host_registry/fixtures/managed-state.json +117 -0
  327. zog/station_access/host_registry/identity.py +158 -0
  328. zog/station_access/host_registry/identity_views.py +165 -0
  329. zog/station_access/host_registry/inventory.py +58 -0
  330. zog/station_access/host_registry/managed_views.py +150 -0
  331. zog/station_access/host_registry/management/__init__.py +0 -0
  332. zog/station_access/host_registry/management/commands/__init__.py +0 -0
  333. zog/station_access/host_registry/management/commands/allow_legacy_host.py +19 -0
  334. zog/station_access/host_registry/management/commands/check_dns_evidence.py +21 -0
  335. zog/station_access/host_registry/management/commands/create_station_vault.py +14 -0
  336. zog/station_access/host_registry/management/commands/enroll_host.py +37 -0
  337. zog/station_access/host_registry/management/commands/manage_dns_destination.py +19 -0
  338. zog/station_access/host_registry/management/commands/manage_host_dns.py +23 -0
  339. zog/station_access/host_registry/management/commands/migrate_host_dns.py +39 -0
  340. zog/station_access/host_registry/management/commands/prune_host_security.py +8 -0
  341. zog/station_access/host_registry/management/commands/reconcile_host_dns.py +20 -0
  342. zog/station_access/host_registry/management/commands/run_deployment_jobs.py +12 -0
  343. zog/station_access/host_registry/management/commands/scan_host_inventory.py +14 -0
  344. zog/station_access/host_registry/matching.py +58 -0
  345. zog/station_access/host_registry/migrations/0001_initial.py +50 -0
  346. zog/station_access/host_registry/migrations/0002_dnsassignment.py +38 -0
  347. zog/station_access/host_registry/migrations/0003_admissionwindow_archivepolicy_enrollmentrequest_and_more.py +98 -0
  348. zog/station_access/host_registry/migrations/0004_security_gate.py +8 -0
  349. zog/station_access/host_registry/migrations/0005_mirrorrole_stationcredential.py +42 -0
  350. zog/station_access/host_registry/migrations/0006_host_archival.py +8 -0
  351. zog/station_access/host_registry/migrations/0007_setup_configuration.py +47 -0
  352. zog/station_access/host_registry/migrations/0008_provider_access_checks.py +29 -0
  353. zog/station_access/host_registry/migrations/0009_cloud_credentials.py +35 -0
  354. zog/station_access/host_registry/migrations/0010_deployment_jobs.py +36 -0
  355. zog/station_access/host_registry/migrations/0011_dns_evidence.py +10 -0
  356. zog/station_access/host_registry/migrations/0012_managed_admission.py +12 -0
  357. zog/station_access/host_registry/migrations/0013_dnsdestination_additionaldnsassignment.py +54 -0
  358. zog/station_access/host_registry/migrations/__init__.py +0 -0
  359. zog/station_access/host_registry/mirror_roles.py +76 -0
  360. zog/station_access/host_registry/models.py +254 -0
  361. zog/station_access/host_registry/operations_views.py +65 -0
  362. zog/station_access/host_registry/provider_checks.py +130 -0
  363. zog/station_access/host_registry/readiness.py +126 -0
  364. zog/station_access/host_registry/retirement.py +129 -0
  365. zog/station_access/host_registry/services.py +26 -0
  366. zog/station_access/host_registry/setup_configuration.py +131 -0
  367. zog/station_access/host_registry/setup_views.py +92 -0
  368. zog/station_access/host_registry/urls.py +6 -0
  369. zog/station_access/host_registry/user_views.py +114 -0
  370. zog/station_access/host_registry/vault.py +64 -0
  371. zog/station_access/host_registry/views.py +247 -0
  372. zog/station_access/management/__init__.py +0 -0
  373. zog/station_access/management/commands/__init__.py +0 -0
  374. zog/station_access/management/commands/export_station_login.py +26 -0
  375. zog/station_access/management/commands/initialize_station.py +37 -0
  376. zog/station_access/management/commands/reconcile_vnc_workspaces.py +21 -0
  377. zog/station_access/migrations/0001_initial.py +80 -0
  378. zog/station_access/migrations/0002_lazy_workspaces.py +36 -0
  379. zog/station_access/migrations/0003_workspace_network_and_sequence.py +17 -0
  380. zog/station_access/migrations/0004_workspace_controller_adapter.py +48 -0
  381. zog/station_access/migrations/__init__.py +0 -0
  382. zog/station_access/models.py +130 -0
  383. zog/station_access/project/__init__.py +0 -0
  384. zog/station_access/project/asgi.py +4 -0
  385. zog/station_access/project/settings.py +136 -0
  386. zog/station_access/project/urls.py +36 -0
  387. zog/station_access/project/wsgi.py +4 -0
  388. zog/station_access/services/__init__.py +0 -0
  389. zog/station_access/services/authorization.py +34 -0
  390. zog/station_access/services/controller_workspaces.py +138 -0
  391. zog/station_access/services/locks.py +24 -0
  392. zog/station_access/services/vnc.py +118 -0
  393. zog/station_access/services/workspace_applications.py +147 -0
  394. zog/station_access/services/workspace_provenance.py +73 -0
  395. zog/station_access/services/workspace_readiness.py +93 -0
  396. zog/station_access/services/workspaces.py +311 -0
  397. zog/station_access/signals.py +25 -0
  398. zog/station_access/web.py +106 -0
  399. zog/version_share/__init__.py +11 -0
  400. zog/version_share/__main__.py +130 -0
  401. zog/version_share/archives.py +145 -0
  402. zog/version_share/core.py +260 -0
  403. zog/version_share/handoffs.py +148 -0
  404. zog/version_share/publication.py +333 -0
  405. zog/version_share/review.py +81 -0
  406. zog/version_share/transfer.py +71 -0
  407. zog/version_share/workspace.py +136 -0
  408. zog-0.1.0.dist-info/METADATA +96 -0
  409. zog-0.1.0.dist-info/RECORD +414 -0
  410. zog-0.1.0.dist-info/WHEEL +5 -0
  411. zog-0.1.0.dist-info/entry_points.txt +17 -0
  412. zog-0.1.0.dist-info/licenses/LICENSES/BSD-3-Clause.txt +11 -0
  413. zog-0.1.0.dist-info/licenses/LICENSES/GPL-3.0-only.txt +232 -0
  414. zog-0.1.0.dist-info/top_level.txt +1 -0
zog/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """Aggregate Zog Python distribution."""
2
+
3
+ __version__ = '0.1.0'
@@ -0,0 +1,142 @@
1
+ {
2
+ "components": [
3
+ {
4
+ "component_dependencies": [],
5
+ "component_version": "0.4.2",
6
+ "program": "archive-mirror",
7
+ "python_package": "zog.archive_mirror",
8
+ "repository": "zog144/archive-mirror",
9
+ "revision": "648691fecc22494dc1aaf8dbe8b2f21b2ed67af3"
10
+ },
11
+ {
12
+ "component_dependencies": [
13
+ "image-build"
14
+ ],
15
+ "component_version": "0.1.0",
16
+ "program": "box-control",
17
+ "python_package": "zog.box_control",
18
+ "repository": "zog144/box-control",
19
+ "revision": "014c8a058889c4b6de1ad58af76cb65ce5a94b7a"
20
+ },
21
+ {
22
+ "component_dependencies": [],
23
+ "component_version": "0.7.2",
24
+ "program": "build-record",
25
+ "python_package": "zog.build_record",
26
+ "repository": "zog144/build-record",
27
+ "revision": "60631ae4c6904e4812bf0d38f8703a6123935aa1"
28
+ },
29
+ {
30
+ "component_dependencies": [
31
+ "box-control",
32
+ "build-record"
33
+ ],
34
+ "component_version": "0.12.0",
35
+ "program": "build-trace",
36
+ "python_package": "zog.build_trace",
37
+ "repository": "zog144/build-trace",
38
+ "revision": "1158030d0a8461554ebeda42ea095b25c10133dc"
39
+ },
40
+ {
41
+ "component_dependencies": [],
42
+ "component_version": "0.10.6",
43
+ "program": "host-deploy",
44
+ "python_package": "zog.host_deploy",
45
+ "repository": "zog144/host-deploy",
46
+ "revision": "21d5368398b9765357fcc180ca037c6c2279f24f"
47
+ },
48
+ {
49
+ "component_dependencies": [
50
+ "host-identify",
51
+ "host-install"
52
+ ],
53
+ "component_version": "0.4.6",
54
+ "program": "host-discover",
55
+ "python_package": "zog.host_discover",
56
+ "repository": "zog144/host-discover",
57
+ "revision": "aef758740ac3a3c47618e3bc6f7e66c16e1eb8d1"
58
+ },
59
+ {
60
+ "component_dependencies": [],
61
+ "component_version": "0.3.4",
62
+ "program": "host-identify",
63
+ "python_package": "zog.host_identify",
64
+ "repository": "zog144/host-identify",
65
+ "revision": "40fd8d901e0434ba4f819a03fa3dc71f9767da64"
66
+ },
67
+ {
68
+ "component_dependencies": [],
69
+ "component_version": "0.4.4",
70
+ "program": "host-install",
71
+ "python_package": "zog.host_install",
72
+ "repository": "zog144/host-install",
73
+ "revision": "e931dc7cc41e7b5a00da862b13f26325c3bd7a17"
74
+ },
75
+ {
76
+ "component_dependencies": [
77
+ "box-control",
78
+ "build-record",
79
+ "host-identify"
80
+ ],
81
+ "component_version": "0.2.0.dev5",
82
+ "program": "image-build",
83
+ "python_package": "zog.image_build",
84
+ "repository": "zog144/image-build",
85
+ "revision": "004bf0eae67a220348926248a06fa6447070cbfb"
86
+ },
87
+ {
88
+ "component_dependencies": [
89
+ "build-trace"
90
+ ],
91
+ "component_version": "0.2.0",
92
+ "program": "integrate-observe",
93
+ "python_package": "zog.integrate_observe",
94
+ "repository": "zog144/integrate-observe",
95
+ "revision": "d20e0ef972ea6f454b7de18f2e541b40e40e32a1"
96
+ },
97
+ {
98
+ "component_dependencies": [],
99
+ "component_version": "0.12.0",
100
+ "program": "network-register",
101
+ "python_package": "zog.network_register",
102
+ "repository": "zog144/network-register",
103
+ "revision": "c9af90c46cc62ad883c113ef0017a71389ee970b"
104
+ },
105
+ {
106
+ "component_dependencies": [
107
+ "box-control"
108
+ ],
109
+ "component_version": "0.1.0",
110
+ "program": "root-control",
111
+ "python_package": "zog.root_control",
112
+ "repository": "zog144/root-control",
113
+ "revision": "3cc6cdc2c5e49232318d82797d3228c6072b9bdc"
114
+ },
115
+ {
116
+ "component_dependencies": [
117
+ "archive-mirror",
118
+ "box-control",
119
+ "host-deploy",
120
+ "host-identify",
121
+ "network-register"
122
+ ],
123
+ "component_version": "0.4.29",
124
+ "program": "station-access",
125
+ "python_package": "zog.station_access",
126
+ "repository": "zog144/station-access",
127
+ "revision": "ab2b1cc0b74787adf7dd81d99b1de5f099fe7460"
128
+ },
129
+ {
130
+ "component_dependencies": [],
131
+ "component_version": "0.4.0",
132
+ "program": "version-share",
133
+ "python_package": "zog.version_share",
134
+ "repository": "zog144/version-share",
135
+ "revision": "5bb90786a46c489abb542ddb80bb2b7a7c8dc218"
136
+ }
137
+ ],
138
+ "distribution": "zog",
139
+ "license_expression": "BSD-3-Clause OR GPL-3.0-only",
140
+ "schema": 1,
141
+ "version": "0.1.0"
142
+ }
@@ -0,0 +1,3 @@
1
+ """Archive-mirror component for the aggregate Zog Python namespace."""
2
+
3
+ __version__ = "0.4.2"
@@ -0,0 +1,2 @@
1
+ from .cli import main
2
+ raise SystemExit(main())
@@ -0,0 +1,6 @@
1
+ from django.apps import AppConfig
2
+
3
+ class ArchiveMirrorConfig(AppConfig):
4
+ name = 'zog.archive_mirror'
5
+ label = 'archive_mirror'
6
+ default_auto_field = 'django.db.models.BigAutoField'
@@ -0,0 +1,91 @@
1
+ import argparse
2
+ import json
3
+ import os
4
+ import sys
5
+
6
+ def main():
7
+ parser=argparse.ArgumentParser()
8
+ sub=parser.add_subparsers(dest='command',required=True)
9
+ sub.add_parser('migrate');sub.add_parser('refresh');sub.add_parser('prune');sub.add_parser('scheduler')
10
+ serving=sub.add_parser('serve');serving.add_argument('--host',default='127.0.0.1');serving.add_argument('--port',type=int,default=8080)
11
+ importing=sub.add_parser('import');importing.add_argument('path');importing.add_argument('--collection',required=True);importing.add_argument('--kind',choices=['source','root-filesystem'],required=True)
12
+ reviewed=sub.add_parser('populate-sources');reviewed.add_argument('manifest')
13
+ release=sub.add_parser('release-source-pin-set');release.add_argument('identity')
14
+ notice=sub.add_parser('notices');notice.add_argument('collection');notice.add_argument('digest')
15
+ evidence=notice.add_mutually_exclusive_group(required=True)
16
+ evidence.add_argument('--source-record');evidence.add_argument('--sidecar');evidence.add_argument('--rootfs-receipts',action='store_true')
17
+ notice.add_argument('--generation');notice.add_argument('--retained-inputs')
18
+ importing.add_argument('--license-sidecar');importing.add_argument('--source-record')
19
+ importing.add_argument('--rootfs-receipts',action='store_true');importing.add_argument('--generation');importing.add_argument('--retained-inputs')
20
+ for name in ('pin','unpin'):
21
+ p=sub.add_parser(name);p.add_argument('collection');p.add_argument('digest');p.add_argument('name')
22
+ args=parser.parse_args()
23
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE','zog.archive_mirror.settings')
24
+ import django;django.setup()
25
+ from . import store
26
+ try:
27
+ if args.command=='migrate':
28
+ from django.core.management import call_command
29
+ call_command('migrate',interactive=False)
30
+ elif args.command=='import':
31
+ record=store.publish(args.path,args.collection,args.kind,{'import':'local','release_approved':False})
32
+ if args.license_sidecar or args.rootfs_receipts or args.source_record:
33
+ attach_notices(record, args.license_sidecar, args.rootfs_receipts, args.generation, args.retained_inputs, args.source_record)
34
+ print(json.dumps({'sha256':record.digest,'bytes':record.size}))
35
+ elif args.command=='populate-sources':
36
+ from .source_inputs import populate
37
+ pin_set,archives=populate(args.manifest)
38
+ print(json.dumps({'pin_set':pin_set.identity,'manifest_sha256':pin_set.manifest_sha256,
39
+ 'active':pin_set.active,'sources':len(archives),'digests':sorted({x.digest for x in archives})}))
40
+ elif args.command=='release-source-pin-set':
41
+ from .source_inputs import deactivate
42
+ pin_set=deactivate(args.identity)
43
+ print(json.dumps({'pin_set':pin_set.identity,'active':pin_set.active}))
44
+ elif args.command=='notices':
45
+ from .models import Archive
46
+ record=Archive.objects.get(collection=args.collection,digest=args.digest)
47
+ print(json.dumps(attach_notices(record,args.sidecar,args.rootfs_receipts,args.generation,args.retained_inputs,args.source_record)))
48
+ elif args.command in ('pin','unpin'):store.pin(args.collection,args.digest,args.name,args.command=='unpin')
49
+ elif args.command=='prune':store.prune()
50
+ elif args.command=='refresh':
51
+ from .collector import collect
52
+ from .config import configuration
53
+ from .lease import lease
54
+ from .models import CollectionStatus
55
+ from django.utils import timezone
56
+ failed=False
57
+ for definition in configuration().get('sources',[]):
58
+ lease()
59
+ status,_=CollectionStatus.objects.update_or_create(source=definition['name'],defaults={'last_attempt':timezone.now()})
60
+ try:
61
+ collect(definition,require_lease=True)
62
+ status.last_success=timezone.now();status.error='';status.save()
63
+ except Exception as error:
64
+ status.error=type(error).__name__;status.save()
65
+ print('Collection failed: '+type(error).__name__,file=sys.stderr);failed=True
66
+ lease();store.prune()
67
+ if failed:raise RuntimeError('Some sources failed')
68
+ elif args.command=='scheduler':
69
+ from .runtime import scheduler
70
+ scheduler()
71
+ elif args.command=='serve':
72
+ from .runtime import server
73
+ server(args.host,args.port)
74
+ except Exception as error:
75
+ print('archive-mirror failed: '+type(error).__name__,file=sys.stderr);return 1
76
+ return 0
77
+
78
+ def attach_notices(record, sidecar, rootfs, generation, retained_inputs, source_record=None):
79
+ from . import notices, notice_contract
80
+ if sum(bool(x) for x in (sidecar, rootfs, source_record)) != 1:
81
+ raise ValueError('Choose one evidence source')
82
+ if source_record:
83
+ from .source_notices import build
84
+ value=build(record,source_record)
85
+ elif rootfs:
86
+ from .receipt_notices import build
87
+ value=build(record,generation,retained_inputs)
88
+ else:
89
+ with open(sidecar,'rb') as stream:
90
+ value=notice_contract.decode(stream.read(notice_contract.MAX_BUNDLE+1))
91
+ return notices.publish(record,value)
@@ -0,0 +1,70 @@
1
+ import datetime
2
+ import lzma
3
+ import os
4
+ import subprocess
5
+ import tarfile
6
+ import tempfile
7
+ from pathlib import Path
8
+ from .models import Snapshot
9
+ from .store import publish
10
+
11
+ def run(arguments, **kwargs):
12
+ environment={**os.environ,'GIT_TERMINAL_PROMPT':'0','GIT_CONFIG_NOSYSTEM':'1','GIT_CONFIG_GLOBAL':'/dev/null','GIT_ALLOW_PROTOCOL':'https:file'}
13
+ return subprocess.run(arguments,check=True,timeout=300,env=environment,stdout=subprocess.PIPE,stderr=subprocess.PIPE,**kwargs).stdout
14
+
15
+ def collect(definition,month=None,allow_local=False,require_lease=False):
16
+ name=definition['name'];url=definition['url'];branch=definition['branch']
17
+ import re
18
+ if not isinstance(name,str) or not re.fullmatch('[a-z0-9][a-z0-9_-]{0,127}',name):raise ValueError('Invalid source name')
19
+ month=month or datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m')
20
+ previous=Snapshot.objects.filter(source=name,month=month).select_related('archive').first()
21
+ if previous:return with_notices(previous.archive, definition)
22
+ if not url.startswith('https://') and not (allow_local and Path(url).is_absolute()):raise ValueError('HTTPS upstream required')
23
+ from urllib.parse import urlsplit
24
+ parts=urlsplit(url)
25
+ if parts.username or parts.password or parts.query or parts.fragment:raise ValueError('Credential-free upstream URL required')
26
+ run(['git','check-ref-format','--branch',branch])
27
+ with tempfile.TemporaryDirectory(prefix='archive-collect-') as work:
28
+ root=Path(work);repo=root/'repository'
29
+ run(['git','clone','--depth','1','--single-branch','--no-tags','--no-checkout','--branch',branch,'--',url,str(repo)])
30
+ commit=run(['git','-C',str(repo),'rev-parse','HEAD']).decode().strip()
31
+ listing=run(['git','-C',str(repo),'ls-tree','-r','-z','HEAD']).split(b'\0')
32
+ files=[]
33
+ if len(listing)>100001:raise ValueError('Too many tracked paths')
34
+ for item in filter(None,listing):
35
+ metadata,path=item.split(b'\t',1);mode,kind,oid=metadata.split()
36
+ if mode==b'160000':raise ValueError('Submodules unsupported in pass 1')
37
+ if path==b'.gitmodules':raise ValueError('Submodules unsupported in pass 1')
38
+ files.append((path,mode,oid))
39
+ archive=root/'source.tar.xz'
40
+ with lzma.open(archive,'wb',preset=6) as compressed,tarfile.open(fileobj=compressed,mode='w|',format=tarfile.PAX_FORMAT) as output:
41
+ import io
42
+ total=0
43
+ for path,mode,oid in sorted(files):
44
+ size=int(run(['git','-C',str(repo),'cat-file','-s',oid.decode()]))
45
+ total+=size
46
+ if size>64*1024**2 or total>2*1024**3:raise ValueError('Source export limit exceeded')
47
+ data=run(['git','-C',str(repo),'cat-file','blob',oid.decode()])
48
+ if data.startswith(b'version https://git-lfs.github.com/spec/v1\n'):raise ValueError('Git LFS unsupported in pass 1')
49
+ text=path.decode('utf-8')
50
+ if text.startswith('/') or '..' in Path(text).parts:raise ValueError('Unsafe tracked path')
51
+ entry=tarfile.TarInfo(name+'/'+text);entry.uid=entry.gid=entry.mtime=0;entry.uname=entry.gname=''
52
+ if mode==b'120000':entry.type=tarfile.SYMTYPE;entry.linkname=data.decode();entry.mode=0o777
53
+ else:entry.mode=0o755 if mode==b'100755' else 0o644;entry.size=len(data)
54
+ output.addfile(entry,io.BytesIO(data) if entry.isfile() else None)
55
+ if require_lease:
56
+ from .lease import lease
57
+ lease()
58
+ result = publish(archive,'sources','source',{'url':url,'branch':branch,'commit':commit,'export_recipe':'tracked-tree-v1','attributes':'ignored'},name,month,commit)
59
+
60
+ return with_notices(result, definition)
61
+
62
+
63
+ def with_notices(archive, definition):
64
+ """Operator-supplied, exact export sidecar; no repository HEAD inference."""
65
+ if definition.get('license_sidecar'):
66
+ from . import notice_contract, notices
67
+ with open(definition['license_sidecar'], 'rb') as stream:
68
+ value = notice_contract.decode(stream.read(notice_contract.MAX_BUNDLE+1))
69
+ notices.publish(archive, value)
70
+ return archive
@@ -0,0 +1,22 @@
1
+ import json
2
+ import os
3
+ import re
4
+ from pathlib import Path
5
+ from django.conf import settings
6
+
7
+ def configuration():
8
+ value = json.loads(Path(settings.ARCHIVE_MIRROR_CONFIGURATION).read_text())
9
+ root = Path(value['root'])
10
+ if not root.is_absolute() or root == Path('/') or root.is_symlink():
11
+ raise ValueError('Explicit persistent archive root required')
12
+ if not isinstance(value['collections'], list) or not value['collections']:
13
+ raise ValueError('Collections required')
14
+ for name in value['collections']:
15
+ if not isinstance(name,str) or not re.fullmatch(r'[a-z0-9][a-z0-9_-]{0,63}', name):
16
+ raise ValueError('Invalid collection')
17
+ return value
18
+
19
+ def collection(name):
20
+ if name not in configuration()['collections']:
21
+ raise ValueError('Unknown collection')
22
+ return name
@@ -0,0 +1,22 @@
1
+ import json
2
+ import os
3
+ import stat
4
+ import time
5
+ import uuid
6
+ from pathlib import Path
7
+ from .config import configuration
8
+
9
+ def lease():
10
+ config=configuration();expected=str(uuid.UUID(config['host_id']))
11
+ path=Path(config['intent_file'])
12
+ fd=os.open(path,os.O_RDONLY|os.O_NOFOLLOW)
13
+ with os.fdopen(fd,'rb') as source:
14
+ info=os.fstat(source.fileno())
15
+ if not stat.S_ISREG(info.st_mode) or info.st_size>32768 or info.st_mode & 0o022:raise ValueError('Unsafe role intent')
16
+ value=json.load(source)
17
+ desired=value['desired'];expires=desired['lease_expires_at']
18
+ if value['version']!=1 or value['host_id']!=expected or desired['version']!=1 or desired['selected'] is not True:
19
+ raise ValueError('Role not selected')
20
+ if type(expires) is not int or not time.time()<expires<=time.time()+905:raise ValueError('Role lease expired')
21
+ if type(desired['revision']) is not int or desired['revision']<1 or desired['endpoint']!=config['endpoint']:raise ValueError('Role binding mismatch')
22
+ return value
File without changes
File without changes
@@ -0,0 +1,55 @@
1
+ # Generated by Django 5.2.17 on 2026-09-25 20:15
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ initial = True
10
+
11
+ dependencies = [
12
+ ]
13
+
14
+ operations = [
15
+ migrations.CreateModel(
16
+ name='Archive',
17
+ fields=[
18
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19
+ ('collection', models.CharField(max_length=64)),
20
+ ('digest', models.CharField(max_length=64)),
21
+ ('size', models.PositiveBigIntegerField()),
22
+ ('kind', models.CharField(max_length=32)),
23
+ ('managed', models.BooleanField(default=False)),
24
+ ('provenance', models.JSONField(default=dict)),
25
+ ('created', models.DateTimeField(auto_now_add=True)),
26
+ ],
27
+ options={
28
+ 'constraints': [models.UniqueConstraint(fields=('collection', 'digest'), name='mirror_collection_digest')],
29
+ },
30
+ ),
31
+ migrations.CreateModel(
32
+ name='Pin',
33
+ fields=[
34
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
35
+ ('name', models.CharField(max_length=200)),
36
+ ('archive', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archive_mirror.archive')),
37
+ ],
38
+ options={
39
+ 'constraints': [models.UniqueConstraint(fields=('archive', 'name'), name='mirror_archive_pin')],
40
+ },
41
+ ),
42
+ migrations.CreateModel(
43
+ name='Snapshot',
44
+ fields=[
45
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
46
+ ('source', models.CharField(max_length=128)),
47
+ ('month', models.CharField(max_length=7)),
48
+ ('commit', models.CharField(max_length=64)),
49
+ ('archive', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='archive_mirror.archive')),
50
+ ],
51
+ options={
52
+ 'constraints': [models.UniqueConstraint(fields=('source', 'month'), name='mirror_source_month')],
53
+ },
54
+ ),
55
+ ]
@@ -0,0 +1,22 @@
1
+ # Generated by Django 5.2.17 on 2026-09-25 20:20
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('archive_mirror', '0001_initial'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.CreateModel(
14
+ name='CollectionStatus',
15
+ fields=[
16
+ ('source', models.CharField(max_length=128, primary_key=True, serialize=False)),
17
+ ('last_attempt', models.DateTimeField()),
18
+ ('last_success', models.DateTimeField(null=True)),
19
+ ('error', models.CharField(blank=True, max_length=100)),
20
+ ],
21
+ ),
22
+ ]
@@ -0,0 +1,33 @@
1
+ # Generated manually for reviewed source pin sets.
2
+ import django.db.models.deletion
3
+ from django.db import migrations, models
4
+
5
+ class Migration(migrations.Migration):
6
+ dependencies = [('archive_mirror', '0002_collectionstatus')]
7
+ operations = [
8
+ migrations.CreateModel(
9
+ name='SourcePinSet',
10
+ fields=[
11
+ ('identity', models.CharField(max_length=180, primary_key=True, serialize=False)),
12
+ ('pin_date', models.DateField()),
13
+ ('manifest_sha256', models.CharField(max_length=64)),
14
+ ('complete', models.BooleanField(default=False)),
15
+ ('active', models.BooleanField(default=False)),
16
+ ('created', models.DateTimeField(auto_now_add=True)),
17
+ ],
18
+ ),
19
+ migrations.CreateModel(
20
+ name='ReviewedSource',
21
+ fields=[
22
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23
+ ('package', models.CharField(blank=True, max_length=200)),
24
+ ('source', models.CharField(blank=True, max_length=200)),
25
+ ('canonical_url', models.TextField()),
26
+ ('archive', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviewed_sources', to='archive_mirror.archive')),
27
+ ('pin_set', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sources', to='archive_mirror.sourcepinset')),
28
+ ],
29
+ options={
30
+ 'constraints': [models.UniqueConstraint(fields=('pin_set','archive','package','source','canonical_url'), name='mirror_reviewed_source_identity')],
31
+ },
32
+ ),
33
+ ]
File without changes
@@ -0,0 +1,51 @@
1
+ from django.db import models
2
+
3
+ class Archive(models.Model):
4
+ collection = models.CharField(max_length=64)
5
+ digest = models.CharField(max_length=64)
6
+ size = models.PositiveBigIntegerField()
7
+ kind = models.CharField(max_length=32)
8
+ managed = models.BooleanField(default=False)
9
+ provenance = models.JSONField(default=dict)
10
+ created = models.DateTimeField(auto_now_add=True)
11
+ class Meta:
12
+ constraints = [models.UniqueConstraint(fields=['collection','digest'], name='mirror_collection_digest')]
13
+
14
+ class Snapshot(models.Model):
15
+ source = models.CharField(max_length=128)
16
+ month = models.CharField(max_length=7)
17
+ archive = models.ForeignKey(Archive, on_delete=models.PROTECT)
18
+ commit = models.CharField(max_length=64)
19
+ class Meta:
20
+ constraints = [models.UniqueConstraint(fields=['source','month'], name='mirror_source_month')]
21
+
22
+ class Pin(models.Model):
23
+ archive = models.ForeignKey(Archive, on_delete=models.CASCADE)
24
+ name = models.CharField(max_length=200)
25
+ class Meta:
26
+ constraints = [models.UniqueConstraint(fields=['archive','name'], name='mirror_archive_pin')]
27
+
28
+ class CollectionStatus(models.Model):
29
+ source = models.CharField(max_length=128, primary_key=True)
30
+ last_attempt = models.DateTimeField()
31
+ last_success = models.DateTimeField(null=True)
32
+ error = models.CharField(max_length=100, blank=True)
33
+
34
+ class SourcePinSet(models.Model):
35
+ identity = models.CharField(max_length=180, primary_key=True)
36
+ pin_date = models.DateField()
37
+ manifest_sha256 = models.CharField(max_length=64)
38
+ complete = models.BooleanField(default=False)
39
+ active = models.BooleanField(default=False)
40
+ created = models.DateTimeField(auto_now_add=True)
41
+
42
+ class ReviewedSource(models.Model):
43
+ pin_set = models.ForeignKey(SourcePinSet, on_delete=models.CASCADE, related_name='sources')
44
+ archive = models.ForeignKey(Archive, on_delete=models.CASCADE, related_name='reviewed_sources')
45
+ package = models.CharField(max_length=200, blank=True)
46
+ source = models.CharField(max_length=200, blank=True)
47
+ canonical_url = models.TextField()
48
+ class Meta:
49
+ constraints = [models.UniqueConstraint(
50
+ fields=['pin_set','archive','package','source','canonical_url'],
51
+ name='mirror_reviewed_source_identity')]