interlock-escrow 1.4.2__tar.gz → 1.4.3__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 (205) hide show
  1. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/CHANGELOG.md +32 -0
  2. interlock_escrow-1.4.3/PKG-INFO +386 -0
  3. interlock_escrow-1.4.3/README.md +353 -0
  4. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/demo.py +8 -8
  5. interlock_escrow-1.4.3/docs/GUIDE.md +927 -0
  6. interlock_escrow-1.4.2/README.md → interlock_escrow-1.4.3/docs/SYSTEMS.md +6 -1206
  7. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/pyproject.toml +9 -2
  8. interlock_escrow-1.4.3/scripts/overhead.py +636 -0
  9. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/__init__.py +1 -1
  10. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_readme_snippets.py +32 -11
  11. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/uv.lock +1 -1
  12. interlock_escrow-1.4.2/PKG-INFO +0 -2563
  13. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/.github/workflows/ci.yml +0 -0
  14. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/.github/workflows/release.yml +0 -0
  15. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/.gitignore +0 -0
  16. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/.python-version +0 -0
  17. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/LICENSE +0 -0
  18. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/SECURITY.md +0 -0
  19. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/demo/customer_acceptance_suite.py +0 -0
  20. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/docs/CUSTOMER_ACCEPTANCE_MATRIX.md +0 -0
  21. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/docs/EPIC3_DESIGN.md +0 -0
  22. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/docs/EPIC4_DESIGN.md +0 -0
  23. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/docs/EPIC5_DESIGN.md +0 -0
  24. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/docs/EPIC6_DESIGN.md +0 -0
  25. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/docs/EPIC7_DESIGN.md +0 -0
  26. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/docs/EPIC8_DESIGN.md +0 -0
  27. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/docs/EPIC9_DESIGN.md +0 -0
  28. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/docs/ESCROW_SPEC.md +0 -0
  29. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/docs/OUTBOX_DESIGN.md +0 -0
  30. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/scripts/kind-setup.sh +0 -0
  31. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/scripts/live_gauntlet.py +0 -0
  32. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/scripts/live_stress_test.py +0 -0
  33. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/scripts/metrics_overhead.py +0 -0
  34. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/scripts/security_audit.py +0 -0
  35. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/adapters.py +0 -0
  36. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/adjudication.py +0 -0
  37. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/anchor.py +0 -0
  38. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/attestations.py +0 -0
  39. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/builder.py +0 -0
  40. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/cascade.py +0 -0
  41. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/chain.py +0 -0
  42. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/checker_packs.py +0 -0
  43. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/cli.py +0 -0
  44. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/cluster.py +0 -0
  45. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/compaction.py +0 -0
  46. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/config.py +0 -0
  47. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/control.py +0 -0
  48. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/daemon.py +0 -0
  49. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/deliveries.py +0 -0
  50. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/engine.py +0 -0
  51. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/exceptions.py +0 -0
  52. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/extension.py +0 -0
  53. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/feedback.py +0 -0
  54. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/fhir.py +0 -0
  55. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/git_substrate.py +0 -0
  56. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/inbox.py +0 -0
  57. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/inbox_sql.py +0 -0
  58. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/inbox_store.py +0 -0
  59. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/invariants.py +0 -0
  60. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/keys.py +0 -0
  61. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/kubernetes.py +0 -0
  62. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/mcp.py +0 -0
  63. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/meters.py +0 -0
  64. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/operators.py +0 -0
  65. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/outbound.py +0 -0
  66. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/outbound_checks.py +0 -0
  67. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/outbox_sql.py +0 -0
  68. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/outbox_store.py +0 -0
  69. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/planbound.py +0 -0
  70. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/postgres.py +0 -0
  71. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/predicted.py +0 -0
  72. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/py.typed +0 -0
  73. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/receipts.py +0 -0
  74. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/reconcile.py +0 -0
  75. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/records.py +0 -0
  76. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/recovery.py +0 -0
  77. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/relay.py +0 -0
  78. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/repair.py +0 -0
  79. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/runtime.py +0 -0
  80. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/sampling.py +0 -0
  81. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/sendgrid.py +0 -0
  82. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/sensors.py +0 -0
  83. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/settlement.py +0 -0
  84. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/signers.py +0 -0
  85. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/sim/__init__.py +0 -0
  86. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/sim/fhir.py +0 -0
  87. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/sim/kubernetes.py +0 -0
  88. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/sim/terraform.py +0 -0
  89. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/sqlite_outbox.py +0 -0
  90. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/stripe.py +0 -0
  91. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/substrate.py +0 -0
  92. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/supervisor.py +0 -0
  93. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/telemetry.py +0 -0
  94. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/terraform.py +0 -0
  95. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/trace.py +0 -0
  96. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/types.py +0 -0
  97. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/vacuum.py +0 -0
  98. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/windows.py +0 -0
  99. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/src/interlock/wiring.py +0 -0
  100. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/__init__.py +0 -0
  101. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/checker_digests.py +0 -0
  102. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/children.py +0 -0
  103. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/conftest.py +0 -0
  104. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/crash_child.py +0 -0
  105. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/daemon_app.py +0 -0
  106. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/fakekms.py +0 -0
  107. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/fakepg.py +0 -0
  108. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/fakesendgrid.py +0 -0
  109. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/fakesink.py +0 -0
  110. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/fakestripe.py +0 -0
  111. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/inbox_child.py +0 -0
  112. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/inbox_env.py +0 -0
  113. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/operator_child.py +0 -0
  114. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/outbox_env.py +0 -0
  115. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/outbox_v2.py +0 -0
  116. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/outbox_v3.py +0 -0
  117. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/outbox_v4.py +0 -0
  118. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/pgbouncer/pgbouncer.ini +0 -0
  119. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/pgbouncer/userlist.txt +0 -0
  120. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/plans.py +0 -0
  121. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/real_systems.py +0 -0
  122. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/relay_child.py +0 -0
  123. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/schemas.py +0 -0
  124. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/settle_child.py +0 -0
  125. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/settling.py +0 -0
  126. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/sqlite_outbox_v3.py +0 -0
  127. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/sqlite_outbox_v4.py +0 -0
  128. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/sqlite_stage_child.py +0 -0
  129. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_acceptance_suite.py +0 -0
  130. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_agreement.py +0 -0
  131. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_anchor.py +0 -0
  132. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_attestations.py +0 -0
  133. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_cascades.py +0 -0
  134. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_checker_packs.py +0 -0
  135. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_checker_records.py +0 -0
  136. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_cluster.py +0 -0
  137. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_cluster_daemon.py +0 -0
  138. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_config.py +0 -0
  139. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_config_fhir.py +0 -0
  140. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_config_plan_bound.py +0 -0
  141. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_core_guarantees.py +0 -0
  142. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_crash_consistency.py +0 -0
  143. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_daemon.py +0 -0
  144. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_deliveries.py +0 -0
  145. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_engine.py +0 -0
  146. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_ergonomics.py +0 -0
  147. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_extension.py +0 -0
  148. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_fact_agreement.py +0 -0
  149. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_feedback.py +0 -0
  150. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_fhir.py +0 -0
  151. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_git_substrate.py +0 -0
  152. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_horizontal.py +0 -0
  153. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_inbox.py +0 -0
  154. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_inbox_cli.py +0 -0
  155. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_inbox_crash.py +0 -0
  156. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_inbox_engine.py +0 -0
  157. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_inbox_vendors.py +0 -0
  158. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_instrumentation.py +0 -0
  159. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_keys.py +0 -0
  160. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_kubernetes.py +0 -0
  161. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_live_sinks.py +0 -0
  162. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_metrics.py +0 -0
  163. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_operator_crash.py +0 -0
  164. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_operators.py +0 -0
  165. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_outbound.py +0 -0
  166. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_outbound_checks.py +0 -0
  167. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_outbox_postgres.py +0 -0
  168. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_pg_upgrade.py +0 -0
  169. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_planbound.py +0 -0
  170. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_pool.py +0 -0
  171. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_postgres.py +0 -0
  172. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_real_fhir.py +0 -0
  173. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_real_kubernetes.py +0 -0
  174. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_real_tofu.py +0 -0
  175. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_reconcile.py +0 -0
  176. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_records.py +0 -0
  177. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_recovery.py +0 -0
  178. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_relay.py +0 -0
  179. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_relay_crash.py +0 -0
  180. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_repair.py +0 -0
  181. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_runtime.py +0 -0
  182. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_sampling.py +0 -0
  183. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_security_audit.py +0 -0
  184. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_sendgrid.py +0 -0
  185. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_sensors.py +0 -0
  186. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_settlement.py +0 -0
  187. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_settlement_crash.py +0 -0
  188. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_shared_transaction.py +0 -0
  189. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_signers.py +0 -0
  190. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_soak.py +0 -0
  191. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_sqlite_outbox.py +0 -0
  192. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_sqlite_stage_crash.py +0 -0
  193. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_stripe.py +0 -0
  194. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_supervisor.py +0 -0
  195. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_takeover.py +0 -0
  196. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_tamper_evidence.py +0 -0
  197. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_terraform.py +0 -0
  198. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_trace.py +0 -0
  199. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_typed_config.py +0 -0
  200. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_upgrade_v5.py +0 -0
  201. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_vacuum.py +0 -0
  202. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_vacuum_crash.py +0 -0
  203. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_vacuum_inbox.py +0 -0
  204. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/test_windows.py +0 -0
  205. {interlock_escrow-1.4.2 → interlock_escrow-1.4.3}/tests/vacuum_child.py +0 -0
@@ -9,6 +9,38 @@ Releases before 0.1.2 are described by their tags and commit history.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [1.4.3] - 2026-11-06
13
+
14
+ Documentation, a benchmark and package metadata. No change to the library, and nothing to
15
+ do to upgrade.
16
+
17
+ ### Added
18
+
19
+ - **`scripts/overhead.py`: what a write costs through Interlock,** on SQLite,
20
+ PostgreSQL, Git, Kubernetes and FHIR (on the loopback servers in `interlock.sim`) and an
21
+ outbound request through the outbox and the relay. Each system is timed directly and
22
+ through the whole path (staged, measured, checked, committed, chained and receipted) in
23
+ alternating rounds. It counts what each write costs in requests, connections, statements
24
+ or git processes, so a reader can add their own network's round trip. The README's table
25
+ replaces "staging roughly doubles write-path round trips".
26
+
27
+ ### Changed
28
+
29
+ - **The README is the front door.** It opens with how AgentGov, Interlock and Fiduciary fit
30
+ together, and links to the live console. It keeps the problem, what Interlock measures
31
+ (now with the four measurement classes T, P, V and R in one table), the lifecycle, the
32
+ quickstart, what a write costs, and install. It is down from 2,535 lines to about 350.
33
+ - **The rest moved, verbatim,** to `docs/GUIDE.md` (the engine) and `docs/SYSTEMS.md` (each
34
+ system, the outbox and the daemon). `test_readme_snippets` now runs every Python block in
35
+ all three files, all 25 of them, as it ran the README's before.
36
+ - **`demo.py` names its five steps "Scenario 1" to "Scenario 5",** not "ACT", as the rest of
37
+ the stack does since 1.4.2.
38
+ - **The development status is "4 - Beta",** not "5 - Production/Stable": a full test suite
39
+ and CI against real PostgreSQL, Kubernetes, OpenTofu and FHIR servers, but no external
40
+ production deployment yet.
41
+ - **The PyPI page links to** the live console, the guide, the repository, the issue tracker
42
+ and the changelog. 1.4.2 and earlier linked to nothing.
43
+
12
44
  ## [1.4.2] - 2026-11-05
13
45
 
14
46
  What an agent is told when a plan is refused, in plainer sentences, and a
@@ -0,0 +1,386 @@
1
+ Metadata-Version: 2.5
2
+ Name: interlock-escrow
3
+ Version: 1.4.3
4
+ Summary: A reference monitor for agent side effects: stage, measure, adjudicate, commit.
5
+ Project-URL: Homepage, https://crimsondevil0929.github.io/fiduciary/
6
+ Project-URL: Documentation, https://github.com/crimsondevil0929/interlock/blob/main/docs/GUIDE.md
7
+ Project-URL: Repository, https://github.com/crimsondevil0929/interlock
8
+ Project-URL: Issues, https://github.com/crimsondevil0929/interlock/issues
9
+ Project-URL: Changelog, https://github.com/crimsondevil0929/interlock/blob/main/CHANGELOG.md
10
+ Author-email: Edward Gu <ygu0929@uchicago.edu>
11
+ License-Expression: Apache-2.0
12
+ License-File: LICENSE
13
+ Keywords: agents,audit,escrow,llm,prompt-injection,reference-monitor,zero-trust
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Security
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: interlock-agentgov>=1.3.0
24
+ Provides-Extra: kms-aws
25
+ Requires-Dist: interlock-agentgov[kms-aws]>=1.3.0; extra == 'kms-aws'
26
+ Provides-Extra: otel
27
+ Requires-Dist: interlock-agentgov[otel]>=1.3.0; extra == 'otel'
28
+ Provides-Extra: postgres
29
+ Requires-Dist: psycopg[binary]>=3.2; extra == 'postgres'
30
+ Provides-Extra: sign
31
+ Requires-Dist: interlock-agentgov[sign]>=1.3.0; extra == 'sign'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Interlock
35
+
36
+ **A reference monitor for agent side effects.** An agent proposes a plan. It holds no
37
+ credentials and cannot execute. Interlock applies the plan against a real but uncommitted
38
+ substrate, measures what actually changed, evaluates deterministic predicates against that
39
+ measurement, and only then commits or rolls back.
40
+
41
+ > **Start here:** [the live console](https://crimsondevil0929.github.io/fiduciary/) ·
42
+ > [how AgentGov, Interlock and Fiduciary fit together](#start-here) ·
43
+ > [put it in front of your agent in 30 minutes](https://github.com/crimsondevil0929/fiduciary/blob/main/docs/QUICKSTART.md)
44
+
45
+ ```bash
46
+ uv run python demo.py
47
+ ```
48
+
49
+ ```
50
+ SCENARIO 2 The identical plan, through Interlock.
51
+ MEASURED by the database, not predicted from the plan:
52
+ rows updated 63 inserted 63 deleted 103
53
+ blast radius 229 rows across 4 tenants
54
+ orders.total $20,925.00 -> $0.00
55
+
56
+ ADJUDICATED by 7 deterministic checks:
57
+ BLOCK blast_radius plan would mutate 229 rows, limit is 8
58
+ BLOCK tenant_isolation plan spans 4 tenants, limit is 1
59
+ BLOCK no_delete plan deletes 103 row(s) from ungranted table(s): order_audit
60
+ BLOCK column_value_guard orders.total falls 100.0% (20,925.00 to 0.00), limit is 30%
61
+ advise stated_footprint agent stated 6 row(s); substrate measured 229 (38.2x)
62
+
63
+ OUTCOME ROLLED BACK
64
+ Byte-identical to before the plan ran. Nothing reached disk.
65
+ ```
66
+
67
+ ## Start here
68
+
69
+ **See the whole stack working:** <https://crimsondevil0929.github.io/fiduciary/>. Six
70
+ industries run on one engine, a real model's recorded turns are replayed on the current
71
+ release, and 16 audit bundles can be verified in your browser.
72
+
73
+ A gateway decides whether an agent may call a tool. This stack decides whether what the call
74
+ changed may be kept, and produces evidence for someone who trusts neither the agent nor the
75
+ operator. It is three packages:
76
+
77
+ | Package | On PyPI | What it answers |
78
+ |---|---|---|
79
+ | [Fiduciary](https://github.com/crimsondevil0929/fiduciary) | [`interlock-fiduciary`](https://pypi.org/project/interlock-fiduciary/) | Who may do what, and who approved each exception. Signed mandates, 2-of-3 quorum warrants, the approvals console, governed MCP tools, and audit bundles anyone can verify offline. **Start here** to put the stack in front of an agent: it installs the other two. |
80
+ | **Interlock** (this repository) | [`interlock-escrow`](https://pypi.org/project/interlock-escrow/) | Does what this write actually changed satisfy policy? It stages the write, measures it, commits it once or puts it back, and delivers outbound calls exactly once. Each change is measured in one of four ways: **T** inside a transaction, **P** as the system's own plan, **V** predicted then read back, **R** the request only. |
81
+ | [AgentGov](https://github.com/crimsondevil0929/agentgov) | [`interlock-agentgov`](https://pypi.org/project/interlock-agentgov/) | May this scope spend this amount? Spend and rate ledgers across a delegation tree, circuit breakers, ARC1 receipts, and reconciliation of a system's own log against them. |
82
+
83
+ New here? [Put it in front of your agent in 30 minutes](https://github.com/crimsondevil0929/fiduciary/blob/main/docs/QUICKSTART.md),
84
+ and read [what it does and does not protect against](https://github.com/crimsondevil0929/fiduciary/blob/main/docs/THREAT_MODEL.md).
85
+
86
+ ## Why authorization does not catch this
87
+
88
+ A prompt-injected agent is a fully authorized agent. It is doing something it has
89
+ permission to do, so an authorization check passes. The injected instruction arrived from
90
+ the customer's own database, inside a tool result, after every provider-side filter had
91
+ already run, so a provider-side filter does not see it either. The only remaining place to
92
+ catch it is between the effect being computed and the effect becoming durable.
93
+
94
+ That position requires write-path credentials to the database the effects land in, which is
95
+ why this runs inside the deployment rather than at the model provider.
96
+
97
+ The demo also rules out model quality as the cause. The agent doesn't loop, costs $0.0012
98
+ and writes valid SQL on its first try. The damage isn't a model failure.
99
+
100
+ **`demo.py` uses a scripted agent, not a live model.** `injected_plan` and the rest of
101
+ scenario 2's input are fixed `EffectPlan`s defined in `demo.py`. No model is called. That keeps
102
+ two questions apart: does Interlock stop this plan, and would a real model produce it? The
103
+ demo answers only the first. Current Claude models decline the injected instruction
104
+ shown above in several realistic phrasings. For runs against a real model, see
105
+ [`scripts/live_gauntlet.py`](scripts/live_gauntlet.py): eight scenarios with a real
106
+ `anthropic.Anthropic` client and governed spend. Its results are not reproducible, because
107
+ model behavior varies.
108
+
109
+ ## What it measures
110
+
111
+ The diff is read **from the substrate**, never reconstructed from the plan. The two are
112
+ routinely different:
113
+
114
+ | | |
115
+ |---|---|
116
+ | Agent stated | 1 row |
117
+ | SQL `rowcount` | 1 row |
118
+ | **Measured** | **2 rows across 2 tables** |
119
+
120
+ An audit trigger that the plan doesn't mention doubled the footprint. Inspecting the plan
121
+ or parsing the SQL can't show that. Reading the change back from the database does.
122
+
123
+ How a change can be read back depends on the system, and each receipt says which of four
124
+ ways it was:
125
+
126
+ | Class | Measured | Where | What it rests on |
127
+ |---|---|---|---|
128
+ | **T**, transactional | inside the transaction, before it commits | SQLite, PostgreSQL | the database's own capture of every row the plan touched |
129
+ | **P**, plan-bound | the system's own plan of the change, applied only if nothing moved | Git (the diff), Kubernetes (the API server's dry run), Terraform (the plan) | the plan applied is the one measured: a compare-and-swap on the head, version or plan file |
130
+ | **V**, predicted and verified | a prediction, written conditionally, then read back | FHIR servers | the read-back: anything that differs from the prediction is put back and receipted `compensated` |
131
+ | **R**, request-only | the request itself | outbound API calls through the outbox | the vendor's word: delivery is receipted, and its own log is reconciled against the receipts |
132
+
133
+ ## Lifecycle
134
+
135
+ ```
136
+ PLANNED -> STAGING -> STAGED -> VERIFIED -> COMMITTED
137
+ \-> REJECTED -> ABORTED
138
+ ```
139
+
140
+ These properties hold by construction:
141
+
142
+ 1. No path from `STAGED` to `COMMITTED` skips adjudication.
143
+ 2. `REJECTED` is not overridable in-process. Overriding means submitting a new plan that
144
+ carries an explicit waiver, which is then itself a recorded artifact.
145
+ 3. A committed effect is discoverable from the chain. `COMMIT_INTENT` is written, and
146
+ forced to stable storage, before the substrate is told to commit, and `COMMITTED`
147
+ after it returns, so a crash in that window leaves an intent with no terminal record
148
+ after it.
149
+ 4. That crash is resolved exactly, without guessing. An intent only says a commit was
150
+ *attempted*, so the substrate also writes a marker row in `_interlock_commits`, inside
151
+ the stage's own transaction. The marker exists if and only if the effects do. On
152
+ restart, `EscrowEngine.recover()` reads the marker for every intent a crashed process
153
+ left open and appends `COMMITTED` or `ABORTED`, noted as recovered. `EscrowRuntime`
154
+ runs it at startup whenever it has a `chain_path`. An intent written before v0.1.2, or
155
+ by a substrate without markers, is left open and logged for an operator, because a
156
+ missing marker proves nothing there. So is one the server hasn't decided yet: a client
157
+ that died mid-`COMMIT` leaves PostgreSQL still committing. A later `recover()` resolves
158
+ it.
159
+ 5. A commit whose answer is lost is resolved the same way while the process is alive. If
160
+ the connection drops with `COMMIT` in flight, the engine reads the marker at once. The
161
+ plan is reported committed if it landed, and rolled back only once the server has ended
162
+ the transaction without it. Until the server decides, `execute()` raises
163
+ `CommitUnsettledError` and writes no terminal record. Don't retry the plan then. The
164
+ intent stays open, and `recover()` settles it.
165
+
166
+ ## Quickstart
167
+
168
+ `EscrowRuntime` owns the substrate, the record chain, the AgentGov anchor and
169
+ the engine. Five lines to a governed write:
170
+
171
+ ```python
172
+ from interlock import EscrowRuntime, TableSpec
173
+
174
+ runtime = EscrowRuntime(
175
+ "prod.db",
176
+ tables=[TableSpec("orders", columns=["id", "tenant", "total"], tenant_column="tenant")],
177
+ scope_id="support-agent",
178
+ )
179
+ result = runtime.execute_sql(
180
+ "UPDATE orders SET total = :total WHERE id = :id",
181
+ {"total": 480.0, "id": 1},
182
+ tenant_id="acme",
183
+ stated_rows=1,
184
+ )
185
+ print(result.committed, result.blocked_by)
186
+ ```
187
+
188
+ That stages the statement in a real transaction, measures what the database
189
+ actually did, adjudicates the measurement, and commits or rolls back.
190
+
191
+ The runtime takes every option the engine does: `substrate=` in place of the
192
+ path (any substrate, PostgreSQL included), `windows=`, `inbox=`, `sinks=`, and
193
+ `anchor=` for claim and settle. `EscrowRuntime.from_config(config, scope_id=...)`
194
+ builds the one `interlock.toml` describes.
195
+
196
+ **Placeholders must be named.** `Effect.parameters` is a `Mapping`, so the
197
+ driver binds by name: write `:id`, never `?`. A positional placeholder is
198
+ refused when the plan is built, before any lock is taken.
199
+
200
+ ### Building a plan
201
+
202
+ For anything past one statement, `PlanBuilder` mints the identifiers and the
203
+ UTC timestamp and wires the dependency DAG:
204
+
205
+ ```python
206
+ from interlock import PlanBuilder
207
+
208
+ plan = (
209
+ PlanBuilder(scope_id="support-agent")
210
+ .update(
211
+ table="orders",
212
+ statement="UPDATE orders SET total = :total WHERE id = :id",
213
+ parameters={"total": 100.0, "id": 1},
214
+ tenant_id="acme",
215
+ stated_rows=1,
216
+ )
217
+ .build()
218
+ )
219
+ ```
220
+
221
+ Effects are sequential by default: each depends on the one added before it.
222
+ Pass `independent=True` for no ordering constraint, or `after=[effect_id]` to
223
+ fan out. `builder.last_effect_id` hands back the id to depend on.
224
+
225
+ ### Under the runtime
226
+
227
+ The primitives are unchanged and the runtime hands them back
228
+ (`runtime.engine`, `runtime.chain`, `runtime.anchor`). Wire them yourself when
229
+ you need to:
230
+
231
+ ```python
232
+ from interlock import (
233
+ BlastRadius,
234
+ EscrowChain,
235
+ EscrowEngine,
236
+ PlanBuilder,
237
+ SqliteSubstrate,
238
+ TableSpec,
239
+ TenantIsolation,
240
+ )
241
+
242
+ substrate = SqliteSubstrate(
243
+ "prod.db",
244
+ tables=[TableSpec("orders", columns=["id", "tenant", "total"], tenant_column="tenant")],
245
+ )
246
+ engine = EscrowEngine(
247
+ substrate,
248
+ checkers=[BlastRadius(50), TenantIsolation(1)],
249
+ chain=EscrowChain("escrow.jsonl"), # omit this and the chain is in memory only
250
+ )
251
+
252
+ plan = (
253
+ PlanBuilder(scope_id="support-agent", intent="refund order 1")
254
+ .update(
255
+ table="orders",
256
+ statement="UPDATE orders SET total = :total WHERE id = :id",
257
+ parameters={"total": 480.0, "id": 1},
258
+ tenant_id="acme",
259
+ stated_rows=1,
260
+ )
261
+ .build()
262
+ )
263
+
264
+ result = engine.execute(plan)
265
+ if not result.committed:
266
+ print(result.blocked_by, result.diff.blast_radius)
267
+ ```
268
+
269
+ `execute()` takes a per-plan `settle_cost=` that overrides the engine's
270
+ default, because what a plan cost to produce is a property of the plan.
271
+
272
+ **The chain file.** `EscrowChain(path)` resumes an existing file. Every record is read and
273
+ verified before the first append, so a restarted process continues the chain instead of
274
+ starting a second one at sequence 1. One file has one writer. The chain claims
275
+ `<path>.lock` for as long as it's open, and a second writer, in this process or another,
276
+ gets `ChainInUseError`. Two writers would fork the chain, and recovery would treat one
277
+ writer's in-flight commits as if it had died. `EscrowChain.load(path)` reads and verifies a
278
+ live chain without claiming it, and can't append. If a crash tears the last line
279
+ mid-append, the next writer cuts it off, because that append never returned. Intents and
280
+ outcomes are fsynced before `append` returns. Pass `fsync=False` to trade that for speed.
281
+
282
+ ## What a write costs
283
+
284
+ Each system timed two ways, in alternating rounds on one machine: the write made directly,
285
+ and the same change through Interlock. Through Interlock is the whole path: staged,
286
+ measured, judged by three checkers, committed, recorded in the escrow chain, and receipted
287
+ with an Ed25519 signature. Measured with [`scripts/overhead.py`](scripts/overhead.py)
288
+ on an Apple M2 Pro (macOS, Python 3.11), PostgreSQL 16 in Docker on the same machine, and
289
+ Kubernetes and FHIR on Interlock's loopback servers.
290
+
291
+ | System | Measured as | Direct, median ms | Through Interlock, median ms | Through Interlock, p95 ms | Added, ms | Ratio | Counted per write (direct / through Interlock) |
292
+ |---|---|---|---|---|---|---|---|
293
+ | SQLite | T, in the transaction | 0.20 | 2.17 | 2.93 | +1.97 | 10.6x | file |
294
+ | PostgreSQL 16 | T, in the transaction | 0.96 | 31.93 | 38.56 | +30.98 | 33.4x | connections: 0 / 1 (7.7 ms each here); statements: 1 / 40 |
295
+ | Git | P, the branch's own diff | 122.19 | 382.55 | 464.59 | +260.36 | 3.1x | git processes: 3 / 16 |
296
+ | Kubernetes (loopback API server) | P, the server's dry run | 1.15 | 5.85 | 6.83 | +4.70 | 5.1x | HTTP requests: 1 / 3 |
297
+ | FHIR R4 (loopback server) | V, predicted, then read back | 2.36 | 9.11 | 10.41 | +6.75 | 3.9x | HTTP requests: 2 / 5 |
298
+ | Outbound request (outbox and relay) | R, the request only | 1.08 | 4.42 | 5.11 | +3.34 | 4.1x | HTTP requests: 1 / 1, sent after the commit |
299
+
300
+ 200 writes per path (Git: 50); interlock 1.4.3.
301
+
302
+ How to read it:
303
+
304
+ - **SQLite: about 2 ms a write.** The stage, the checks, the chain record and the signed
305
+ receipt, with no network.
306
+ - **PostgreSQL: about 31 ms here, and most of it is round trips.** A stage opens its own
307
+ connection (7.7 ms through Docker's port forwarding) and runs 40 statements, where the
308
+ direct write is one UPDATE on a connection already open. Each statement is a round trip,
309
+ so on a database across a network, multiply 40 by that network's round trip. Stages do
310
+ not reuse connections yet.
311
+ - **Git: git's own processes.** 16 of them against 3 for a commit and push.
312
+ - **Kubernetes and FHIR: two and three more requests.** The dry run and the read-back on
313
+ Kubernetes; the read, the conditional write and the read-back on FHIR. Against a real
314
+ server, each costs that network's round trip.
315
+ - **Outbound: the relay's delivery after the commit,** about 3 ms on top of the POST itself.
316
+
317
+ An agent's tool call already waits hundreds of milliseconds to seconds on its model, so on
318
+ most systems this is a small part of each step. A read never goes through Interlock at all.
319
+ Run `uv run python scripts/overhead.py --postgres "$DSN" --markdown` for your own numbers.
320
+
321
+ ## Install
322
+
323
+ ```bash
324
+ pip install interlock-escrow # or, with the stack above it: pip install interlock-fiduciary
325
+ ```
326
+
327
+ To work on it, clone it. `agentgov` comes from PyPI (`interlock-agentgov>=1.3.0`, ARC1
328
+ 1.4), so a clone needs nothing beside it:
329
+
330
+ ```bash
331
+ git clone https://github.com/crimsondevil0929/interlock
332
+ cd interlock && uv sync
333
+ ```
334
+
335
+ Signing (a relay's attestations, an operator's records) needs the `sign` extra
336
+ (`cryptography`). Verifying never does. `kms-aws` adds `boto3` for `AwsKmsSigner`, a key
337
+ held by AWS KMS, and `otel` adds agentgov's OpenTelemetry metrics. Both pass through to
338
+ agentgov's extras of the same names.
339
+
340
+ **Developing on both repos at once.** `uv sync` resolves agentgov from PyPI, and a sibling
341
+ `../agentgov` checkout is not consulted on its own. To point `uv` at one, add to
342
+ interlock's `pyproject.toml`:
343
+
344
+ ```toml
345
+ [tool.uv.sources]
346
+ interlock-agentgov = { path = "../agentgov", editable = true }
347
+ ```
348
+
349
+ then `uv lock && uv sync`. It is workspace configuration, not written into the built
350
+ wheel. Drop it, and rerun `uv lock`, before merging: CI resolves a clean clone from the
351
+ lockfile, with no sibling checkout, and fails on a path source.
352
+
353
+ ## Development
354
+
355
+ ```bash
356
+ uv sync
357
+ uv run pytest -q
358
+ uv run ruff check . && uv run ruff format --check .
359
+ uv run mypy src/ && uv run mypy --strict tests/ demo.py
360
+ ```
361
+
362
+ The PostgreSQL tests run against a live server. Set `INTERLOCK_TEST_POSTGRES_DSN` to a
363
+ role that may create databases and roles, and `INTERLOCK_REQUIRE_POSTGRES=1` to fail
364
+ rather than skip when there isn't one. Two suites are release gates.
365
+ `tests/test_crash_consistency.py` kills a real process with `SIGKILL` at every step of a
366
+ PostgreSQL commit, and at random instants, then checks exactly what recovery makes of it.
367
+ `tests/test_tamper_evidence.py` alters every part of the audit trail and checks that
368
+ verification fails exactly there. `INTERLOCK_CRASH_SEED` and `INTERLOCK_CRASH_ROUNDS`
369
+ replay or lengthen the random-instant run.
370
+
371
+ ## License
372
+
373
+ Apache-2.0: see [LICENSE](LICENSE). To report a vulnerability, see [SECURITY.md](SECURITY.md).
374
+
375
+ ## Documentation
376
+
377
+ - [docs/GUIDE.md](docs/GUIDE.md): the engine in full. Typing, invariants and partitions,
378
+ refusals and repair, rate windows, AgentGov and Fiduciary, receipts, recovery, extension
379
+ quotes, and [what Interlock does and does not claim](docs/GUIDE.md#scope).
380
+ - [docs/SYSTEMS.md](docs/SYSTEMS.md): each system it stages on (PostgreSQL, Git, Kubernetes
381
+ and Terraform, FHIR), outbound requests through the outbox, and the daemon.
382
+ - [docs/ESCROW_SPEC.md](docs/ESCROW_SPEC.md): the specification, with every requirement
383
+ traced to its tests. [docs/OUTBOX_DESIGN.md](docs/OUTBOX_DESIGN.md) and the `EPIC*_DESIGN`
384
+ documents record how each part was designed.
385
+ - [scripts/overhead.py](scripts/overhead.py): the overhead table above, reproducible.
386
+ - [CHANGELOG.md](CHANGELOG.md), [SECURITY.md](SECURITY.md).