interlock-fiduciary 1.0.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 (200) hide show
  1. interlock_fiduciary-1.0.0/.github/workflows/ci.yml +159 -0
  2. interlock_fiduciary-1.0.0/.github/workflows/release.yml +128 -0
  3. interlock_fiduciary-1.0.0/.gitignore +38 -0
  4. interlock_fiduciary-1.0.0/CHANGELOG.md +505 -0
  5. interlock_fiduciary-1.0.0/LICENSE +191 -0
  6. interlock_fiduciary-1.0.0/PKG-INFO +504 -0
  7. interlock_fiduciary-1.0.0/README.md +474 -0
  8. interlock_fiduciary-1.0.0/docs/BUNDLE_FORMAT.md +283 -0
  9. interlock_fiduciary-1.0.0/docs/FIDUCIARY_SPEC.md +2316 -0
  10. interlock_fiduciary-1.0.0/docs/MUTATION_PASS.md +91 -0
  11. interlock_fiduciary-1.0.0/pyproject.toml +103 -0
  12. interlock_fiduciary-1.0.0/scripts/ilok1_sizes.py +183 -0
  13. interlock_fiduciary-1.0.0/scripts/live_acceptance.py +798 -0
  14. interlock_fiduciary-1.0.0/scripts/live_stress_test.py +1080 -0
  15. interlock_fiduciary-1.0.0/scripts/make_bundles.py +283 -0
  16. interlock_fiduciary-1.0.0/scripts/make_upgrade_stores.py +103 -0
  17. interlock_fiduciary-1.0.0/scripts/make_vectors.py +424 -0
  18. interlock_fiduciary-1.0.0/scripts/security_audit.py +572 -0
  19. interlock_fiduciary-1.0.0/scripts/upgrade_scenario.py +169 -0
  20. interlock_fiduciary-1.0.0/src/fiduciary/__init__.py +38 -0
  21. interlock_fiduciary-1.0.0/src/fiduciary/_upstream.py +38 -0
  22. interlock_fiduciary-1.0.0/src/fiduciary/authority.py +1402 -0
  23. interlock_fiduciary-1.0.0/src/fiduciary/binding.py +629 -0
  24. interlock_fiduciary-1.0.0/src/fiduciary/bridge.py +1522 -0
  25. interlock_fiduciary-1.0.0/src/fiduciary/bundle/__init__.py +24 -0
  26. interlock_fiduciary-1.0.0/src/fiduciary/bundle/export.py +579 -0
  27. interlock_fiduciary-1.0.0/src/fiduciary/bundle/format.py +164 -0
  28. interlock_fiduciary-1.0.0/src/fiduciary/bundle/verify.py +1169 -0
  29. interlock_fiduciary-1.0.0/src/fiduciary/checkers.py +160 -0
  30. interlock_fiduciary-1.0.0/src/fiduciary/cli.py +1465 -0
  31. interlock_fiduciary-1.0.0/src/fiduciary/compiler.py +733 -0
  32. interlock_fiduciary-1.0.0/src/fiduciary/config.py +920 -0
  33. interlock_fiduciary-1.0.0/src/fiduciary/documents/__init__.py +95 -0
  34. interlock_fiduciary-1.0.0/src/fiduciary/documents/_base.py +149 -0
  35. interlock_fiduciary-1.0.0/src/fiduciary/documents/approval.py +114 -0
  36. interlock_fiduciary-1.0.0/src/fiduciary/documents/charter.py +158 -0
  37. interlock_fiduciary-1.0.0/src/fiduciary/documents/mandate.py +1373 -0
  38. interlock_fiduciary-1.0.0/src/fiduciary/documents/record.py +138 -0
  39. interlock_fiduciary-1.0.0/src/fiduciary/documents/ticket.py +1021 -0
  40. interlock_fiduciary-1.0.0/src/fiduciary/documents/tool.py +391 -0
  41. interlock_fiduciary-1.0.0/src/fiduciary/documents/warrant.py +256 -0
  42. interlock_fiduciary-1.0.0/src/fiduciary/escalation.py +706 -0
  43. interlock_fiduciary-1.0.0/src/fiduciary/exceptions.py +85 -0
  44. interlock_fiduciary-1.0.0/src/fiduciary/executor.py +975 -0
  45. interlock_fiduciary-1.0.0/src/fiduciary/fidu1.py +432 -0
  46. interlock_fiduciary-1.0.0/src/fiduciary/keys.py +130 -0
  47. interlock_fiduciary-1.0.0/src/fiduciary/log.py +273 -0
  48. interlock_fiduciary-1.0.0/src/fiduciary/mcp.py +749 -0
  49. interlock_fiduciary-1.0.0/src/fiduciary/postgres.py +372 -0
  50. interlock_fiduciary-1.0.0/src/fiduciary/py.typed +0 -0
  51. interlock_fiduciary-1.0.0/src/fiduciary/refines.py +508 -0
  52. interlock_fiduciary-1.0.0/src/fiduciary/service.py +955 -0
  53. interlock_fiduciary-1.0.0/src/fiduciary/sqlite.py +303 -0
  54. interlock_fiduciary-1.0.0/src/fiduciary/store.py +787 -0
  55. interlock_fiduciary-1.0.0/src/fiduciary/tools.py +182 -0
  56. interlock_fiduciary-1.0.0/src/fiduciary/verify.py +252 -0
  57. interlock_fiduciary-1.0.0/tests/__init__.py +0 -0
  58. interlock_fiduciary-1.0.0/tests/bound.py +385 -0
  59. interlock_fiduciary-1.0.0/tests/bridge_kit.py +178 -0
  60. interlock_fiduciary-1.0.0/tests/bundle_kit.py +136 -0
  61. interlock_fiduciary-1.0.0/tests/compiled_digests.py +30 -0
  62. interlock_fiduciary-1.0.0/tests/conftest.py +218 -0
  63. interlock_fiduciary-1.0.0/tests/contention_child.py +57 -0
  64. interlock_fiduciary-1.0.0/tests/crash_child.py +44 -0
  65. interlock_fiduciary-1.0.0/tests/crash_env.py +425 -0
  66. interlock_fiduciary-1.0.0/tests/deploy.py +55 -0
  67. interlock_fiduciary-1.0.0/tests/examples.py +518 -0
  68. interlock_fiduciary-1.0.0/tests/fakes.py +130 -0
  69. interlock_fiduciary-1.0.0/tests/serve_kit.py +248 -0
  70. interlock_fiduciary-1.0.0/tests/test_adversarial_agent.py +234 -0
  71. interlock_fiduciary-1.0.0/tests/test_authority.py +468 -0
  72. interlock_fiduciary-1.0.0/tests/test_binding.py +731 -0
  73. interlock_fiduciary-1.0.0/tests/test_bridge.py +456 -0
  74. interlock_fiduciary-1.0.0/tests/test_bridge_edges.py +370 -0
  75. interlock_fiduciary-1.0.0/tests/test_bridge_lifecycle.py +227 -0
  76. interlock_fiduciary-1.0.0/tests/test_bundle.py +84 -0
  77. interlock_fiduciary-1.0.0/tests/test_bundle_checks.py +724 -0
  78. interlock_fiduciary-1.0.0/tests/test_bundle_export.py +128 -0
  79. interlock_fiduciary-1.0.0/tests/test_bundle_tamper.py +269 -0
  80. interlock_fiduciary-1.0.0/tests/test_bundle_vectors.py +52 -0
  81. interlock_fiduciary-1.0.0/tests/test_cli.py +512 -0
  82. interlock_fiduciary-1.0.0/tests/test_cli_binding.py +375 -0
  83. interlock_fiduciary-1.0.0/tests/test_cli_bundle.py +182 -0
  84. interlock_fiduciary-1.0.0/tests/test_cli_mcp.py +359 -0
  85. interlock_fiduciary-1.0.0/tests/test_cli_tickets.py +127 -0
  86. interlock_fiduciary-1.0.0/tests/test_cluster.py +292 -0
  87. interlock_fiduciary-1.0.0/tests/test_compiler.py +453 -0
  88. interlock_fiduciary-1.0.0/tests/test_config.py +359 -0
  89. interlock_fiduciary-1.0.0/tests/test_contention.py +218 -0
  90. interlock_fiduciary-1.0.0/tests/test_crash_recovery.py +271 -0
  91. interlock_fiduciary-1.0.0/tests/test_decide.py +491 -0
  92. interlock_fiduciary-1.0.0/tests/test_determinism.py +64 -0
  93. interlock_fiduciary-1.0.0/tests/test_documents.py +633 -0
  94. interlock_fiduciary-1.0.0/tests/test_escalation_quorum.py +950 -0
  95. interlock_fiduciary-1.0.0/tests/test_executor.py +1293 -0
  96. interlock_fiduciary-1.0.0/tests/test_fidu1.py +315 -0
  97. interlock_fiduciary-1.0.0/tests/test_ilok1_sizes.py +74 -0
  98. interlock_fiduciary-1.0.0/tests/test_live_acceptance.py +221 -0
  99. interlock_fiduciary-1.0.0/tests/test_mcp.py +572 -0
  100. interlock_fiduciary-1.0.0/tests/test_monotonicity.py +345 -0
  101. interlock_fiduciary-1.0.0/tests/test_postgres.py +178 -0
  102. interlock_fiduciary-1.0.0/tests/test_readme.py +42 -0
  103. interlock_fiduciary-1.0.0/tests/test_refines.py +1211 -0
  104. interlock_fiduciary-1.0.0/tests/test_resolve.py +523 -0
  105. interlock_fiduciary-1.0.0/tests/test_security_audit.py +68 -0
  106. interlock_fiduciary-1.0.0/tests/test_service.py +382 -0
  107. interlock_fiduciary-1.0.0/tests/test_sessions.py +182 -0
  108. interlock_fiduciary-1.0.0/tests/test_signers.py +97 -0
  109. interlock_fiduciary-1.0.0/tests/test_soak.py +51 -0
  110. interlock_fiduciary-1.0.0/tests/test_state.py +236 -0
  111. interlock_fiduciary-1.0.0/tests/test_store.py +395 -0
  112. interlock_fiduciary-1.0.0/tests/test_tamper.py +409 -0
  113. interlock_fiduciary-1.0.0/tests/test_tamper_escalation.py +322 -0
  114. interlock_fiduciary-1.0.0/tests/test_tickets_warrants.py +387 -0
  115. interlock_fiduciary-1.0.0/tests/test_tools.py +89 -0
  116. interlock_fiduciary-1.0.0/tests/test_upgrade.py +223 -0
  117. interlock_fiduciary-1.0.0/tests/test_vectors.py +248 -0
  118. interlock_fiduciary-1.0.0/tests/upgrades/0.1.0/fiduciary.sql +48 -0
  119. interlock_fiduciary-1.0.0/tests/upgrades/0.1.0/ledger.sql +86 -0
  120. interlock_fiduciary-1.0.0/tests/upgrades/0.1.0/manifest.json +5 -0
  121. interlock_fiduciary-1.0.0/tests/upgrades/0.1.0/postgres.json +39 -0
  122. interlock_fiduciary-1.0.0/tests/upgrades/0.2.0/chains/24115ee4-600f-462b-b58d-d57309c62ad5.chain +0 -0
  123. interlock_fiduciary-1.0.0/tests/upgrades/0.2.0/chains/2d0b5f7e-8a43-4c2b-b1a4-0c4e2f9a7d31.chain +6 -0
  124. interlock_fiduciary-1.0.0/tests/upgrades/0.2.0/fiduciary.sql +66 -0
  125. interlock_fiduciary-1.0.0/tests/upgrades/0.2.0/ledger.sql +106 -0
  126. interlock_fiduciary-1.0.0/tests/upgrades/0.2.0/manifest.json +6 -0
  127. interlock_fiduciary-1.0.0/tests/upgrades/0.2.0/postgres.json +40 -0
  128. interlock_fiduciary-1.0.0/tests/upgrades/0.2.0/receipts.jsonl +1 -0
  129. interlock_fiduciary-1.0.0/tests/upgrades/0.2.0/tools/675784cb90ac4a1e109e487781a9db86c87d5aed2886a8a43baaf60f5d1a74f6.json +29 -0
  130. interlock_fiduciary-1.0.0/tests/upgrades/0.2.0/tools/da3867841890b66c16218628550d133c2ad3a270a7283eefab80110679d87d41.json +83 -0
  131. interlock_fiduciary-1.0.0/tests/upgrades/0.3.0/chains/0f7222eb-e65e-4832-939b-a1a7126a44f0.chain +0 -0
  132. interlock_fiduciary-1.0.0/tests/upgrades/0.3.0/chains/2d0b5f7e-8a43-4c2b-b1a4-0c4e2f9a7d31.chain +6 -0
  133. interlock_fiduciary-1.0.0/tests/upgrades/0.3.0/fiduciary.sql +88 -0
  134. interlock_fiduciary-1.0.0/tests/upgrades/0.3.0/ledger.sql +112 -0
  135. interlock_fiduciary-1.0.0/tests/upgrades/0.3.0/manifest.json +6 -0
  136. interlock_fiduciary-1.0.0/tests/upgrades/0.3.0/operators.jsonl +2 -0
  137. interlock_fiduciary-1.0.0/tests/upgrades/0.3.0/postgres.json +43 -0
  138. interlock_fiduciary-1.0.0/tests/upgrades/0.3.0/receipts.jsonl +1 -0
  139. interlock_fiduciary-1.0.0/tests/upgrades/0.3.0/tools/675784cb90ac4a1e109e487781a9db86c87d5aed2886a8a43baaf60f5d1a74f6.json +29 -0
  140. interlock_fiduciary-1.0.0/tests/upgrades/0.3.0/tools/da3867841890b66c16218628550d133c2ad3a270a7283eefab80110679d87d41.json +83 -0
  141. interlock_fiduciary-1.0.0/tests/upgrades/0.4.0/chains/2d0b5f7e-8a43-4c2b-b1a4-0c4e2f9a7d31.chain +12 -0
  142. interlock_fiduciary-1.0.0/tests/upgrades/0.4.0/chains/4ecba1eb-7db4-45c2-8324-f73940563e09.chain +0 -0
  143. interlock_fiduciary-1.0.0/tests/upgrades/0.4.0/fiduciary.sql +93 -0
  144. interlock_fiduciary-1.0.0/tests/upgrades/0.4.0/ledger.sql +120 -0
  145. interlock_fiduciary-1.0.0/tests/upgrades/0.4.0/manifest.json +6 -0
  146. interlock_fiduciary-1.0.0/tests/upgrades/0.4.0/operators.jsonl +2 -0
  147. interlock_fiduciary-1.0.0/tests/upgrades/0.4.0/postgres.json +44 -0
  148. interlock_fiduciary-1.0.0/tests/upgrades/0.4.0/receipts.jsonl +2 -0
  149. interlock_fiduciary-1.0.0/tests/upgrades/0.4.0/tools/675784cb90ac4a1e109e487781a9db86c87d5aed2886a8a43baaf60f5d1a74f6.json +29 -0
  150. interlock_fiduciary-1.0.0/tests/upgrades/0.4.0/tools/9595bcef244b5c75b978983e33c746b9df31895acde67551b9e70edd32ad8888.json +66 -0
  151. interlock_fiduciary-1.0.0/tests/upgrades/0.4.0/tools/b06cf07de22be069688bd2816074439ad46e03e0d63d8a971a6a5da1de16135e.json +81 -0
  152. interlock_fiduciary-1.0.0/tests/upgrades/0.4.0/tools/da3867841890b66c16218628550d133c2ad3a270a7283eefab80110679d87d41.json +83 -0
  153. interlock_fiduciary-1.0.0/tests/upgrades/0.5.0/chains/2d0b5f7e-8a43-4c2b-b1a4-0c4e2f9a7d31.chain +12 -0
  154. interlock_fiduciary-1.0.0/tests/upgrades/0.5.0/chains/77b04b25-c6b3-4348-ab04-1345e061027a.chain +0 -0
  155. interlock_fiduciary-1.0.0/tests/upgrades/0.5.0/fiduciary.sql +93 -0
  156. interlock_fiduciary-1.0.0/tests/upgrades/0.5.0/ledger.sql +120 -0
  157. interlock_fiduciary-1.0.0/tests/upgrades/0.5.0/manifest.json +6 -0
  158. interlock_fiduciary-1.0.0/tests/upgrades/0.5.0/operators.jsonl +2 -0
  159. interlock_fiduciary-1.0.0/tests/upgrades/0.5.0/postgres.json +44 -0
  160. interlock_fiduciary-1.0.0/tests/upgrades/0.5.0/receipts.jsonl +2 -0
  161. interlock_fiduciary-1.0.0/tests/upgrades/0.5.0/tools/675784cb90ac4a1e109e487781a9db86c87d5aed2886a8a43baaf60f5d1a74f6.json +29 -0
  162. interlock_fiduciary-1.0.0/tests/upgrades/0.5.0/tools/9595bcef244b5c75b978983e33c746b9df31895acde67551b9e70edd32ad8888.json +66 -0
  163. interlock_fiduciary-1.0.0/tests/upgrades/0.5.0/tools/b06cf07de22be069688bd2816074439ad46e03e0d63d8a971a6a5da1de16135e.json +81 -0
  164. interlock_fiduciary-1.0.0/tests/upgrades/0.5.0/tools/da3867841890b66c16218628550d133c2ad3a270a7283eefab80110679d87d41.json +83 -0
  165. interlock_fiduciary-1.0.0/tests/upstream_server.py +64 -0
  166. interlock_fiduciary-1.0.0/uv.lock +1113 -0
  167. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/index.json +838 -0
  168. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/unreadable-not-a-tar.fidb +64 -0
  169. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v1-byte-changed.fidb +0 -0
  170. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v1-member-deleted.fidb +0 -0
  171. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v1-member-duplicated.fidb +0 -0
  172. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v1-members-out-of-order.fidb +0 -0
  173. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v10-unanchored-record.fidb +0 -0
  174. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v11-commit-after-revocation.fidb +0 -0
  175. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v12-records-ahead-of-the-ledger.fidb +0 -0
  176. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v2-wrong-pinned-charter.fidb +0 -0
  177. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v3-child-broader-than-parent.fidb +0 -0
  178. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v3-mandate-one-approval-short.fidb +0 -0
  179. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v4-other-deployment.fidb +0 -0
  180. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v5-other-receipt-key.fidb +0 -0
  181. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v6-other-ledger-key.fidb +0 -0
  182. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v6-witness-never-cosigned.fidb +0 -0
  183. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v7-break-glass-release.fidb +0 -0
  184. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v7-manual-release.fidb +0 -0
  185. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v7-raw-reset.fidb +0 -0
  186. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v7-unmandated-delegation.fidb +0 -0
  187. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v7-unmandated-plan.fidb +0 -0
  188. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v8-executed-after-not-after.fidb +0 -0
  189. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v8-false-rule-predicate.fidb +0 -0
  190. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v8-head-moved.fidb +0 -0
  191. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v8-warrant-one-approval-short.fidb +0 -0
  192. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v9-log-edited-manifest-rewritten.fidb +0 -0
  193. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v9-manifest-signed-by-another.fidb +0 -0
  194. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/v9-other-service-key.fidb +0 -0
  195. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/valid-held-release.fidb +0 -0
  196. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/valid-lineage-at-work.fidb +0 -0
  197. interlock_fiduciary-1.0.0/vectors/fidu1/bundles/valid-phase-4-lifecycle.fidb +0 -0
  198. interlock_fiduciary-1.0.0/vectors/fidu1/invalid.json +2834 -0
  199. interlock_fiduciary-1.0.0/vectors/fidu1/records.json +83 -0
  200. interlock_fiduciary-1.0.0/vectors/fidu1/valid.json +816 -0
@@ -0,0 +1,159 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+ workflow_call: # release.yml runs every check before it builds
9
+
10
+ concurrency:
11
+ group: ${{ github.workflow }}-${{ github.ref }}
12
+ cancel-in-progress: true
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ test:
19
+ name: test (py${{ matrix.python-version }}, ${{ matrix.os }})
20
+ runs-on: ${{ matrix.os }}
21
+ timeout-minutes: 30
22
+ env:
23
+ UV_PYTHON: ${{ matrix.python-version }}
24
+ strategy:
25
+ fail-fast: false
26
+ matrix:
27
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
28
+ os: [ubuntu-latest]
29
+ include:
30
+ # SQLite's file locking differs by platform, and the store claims a
31
+ # log's single writer with a lock beside the file.
32
+ - python-version: "3.12"
33
+ os: macos-latest
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ - uses: astral-sh/setup-uv@v5
37
+ with:
38
+ enable-cache: true
39
+ - name: Set up Python ${{ matrix.python-version }}
40
+ run: uv python install ${{ matrix.python-version }}
41
+ # Interlock and AgentGov resolve from PyPI, pinned in uv.lock; --frozen
42
+ # fails a dependency edit that was not re-locked.
43
+ - name: Install the project
44
+ run: uv sync --frozen --python ${{ matrix.python-version }}
45
+ - name: Lint (ruff check)
46
+ run: uv run ruff check .
47
+ - name: Format (ruff format --check)
48
+ run: uv run ruff format --check .
49
+ - name: Type-check the package (mypy --strict)
50
+ run: uv run mypy src/
51
+ - name: Type-check the tests and scripts (mypy --strict)
52
+ run: uv run mypy --strict tests/ scripts/
53
+ - name: The golden vectors are what the generator writes
54
+ run: |
55
+ uv run python scripts/make_vectors.py
56
+ git diff --exit-code vectors/
57
+ - name: The spec's ILOK1 sizes are what interlock signs (F3-9)
58
+ run: uv run python scripts/ilok1_sizes.py --check
59
+ - name: Tests with coverage
60
+ run: uv run pytest -v --cov=fiduciary --cov-branch --cov-report=term-missing
61
+ # postgres.py runs only against a server, which this job has none of; the
62
+ # postgres job below measures everything against the higher floors.
63
+ - name: Enforce coverage floor
64
+ run: uv run coverage report --fail-under=90 --omit=src/fiduciary/postgres.py
65
+
66
+ postgres:
67
+ name: test against PostgreSQL ${{ matrix.postgres }} (py3.12)
68
+ runs-on: ubuntu-latest
69
+ timeout-minutes: 30
70
+ strategy:
71
+ fail-fast: false
72
+ matrix:
73
+ postgres: ["14", "16", "18"]
74
+ services:
75
+ postgres:
76
+ image: postgres:${{ matrix.postgres }}
77
+ env:
78
+ POSTGRES_USER: fiduciary_admin
79
+ POSTGRES_PASSWORD: fiduciary
80
+ ports: ["5432:5432"]
81
+ options: >-
82
+ --health-cmd "pg_isready -U fiduciary_admin"
83
+ --health-interval 2s --health-timeout 5s --health-retries 30
84
+ env:
85
+ UV_PYTHON: "3.12"
86
+ FIDUCIARY_TEST_POSTGRES_DSN: postgresql://fiduciary_admin:fiduciary@localhost:5432/postgres
87
+ FIDUCIARY_REQUIRE_POSTGRES: "1"
88
+ steps:
89
+ - uses: actions/checkout@v4
90
+ - uses: astral-sh/setup-uv@v5
91
+ with:
92
+ enable-cache: true
93
+ - run: uv python install 3.12
94
+ - run: uv sync --frozen --python 3.12
95
+ - name: Tests with coverage, both stores
96
+ run: uv run pytest -v --cov=fiduciary --cov-branch --cov-report=term-missing --cov-report=json
97
+ # The floors: 95% of lines and 90% of branches, measured apart.
98
+ - name: Enforce coverage floors
99
+ run: |
100
+ uv run python - <<'EOF'
101
+ import json, sys
102
+ totals = json.load(open("coverage.json"))["totals"]
103
+ lines = 100 * totals["covered_lines"] / totals["num_statements"]
104
+ branches = 100 * totals["covered_branches"] / totals["num_branches"]
105
+ print(f"lines {lines:.2f}%, branches {branches:.2f}%")
106
+ sys.exit(0 if lines >= 95 and branches >= 90 else 1)
107
+ EOF
108
+
109
+ upstream:
110
+ # The fixes of the spec's section 12 land on interlock's and AgentGov's main
111
+ # before a release. Here the tests that need them run, and those of each
112
+ # fallback skip: the jobs above hold the fallbacks, on the releases uv.lock pins.
113
+ name: test against interlock and AgentGov main (py3.12, PostgreSQL 16)
114
+ runs-on: ubuntu-latest
115
+ timeout-minutes: 30
116
+ services:
117
+ postgres:
118
+ image: postgres:16
119
+ env:
120
+ POSTGRES_USER: fiduciary_admin
121
+ POSTGRES_PASSWORD: fiduciary
122
+ ports: ["5432:5432"]
123
+ options: >-
124
+ --health-cmd "pg_isready -U fiduciary_admin"
125
+ --health-interval 2s --health-timeout 5s --health-retries 30
126
+ env:
127
+ UV_PYTHON: "3.12"
128
+ FIDUCIARY_TEST_POSTGRES_DSN: postgresql://fiduciary_admin:fiduciary@localhost:5432/postgres
129
+ FIDUCIARY_REQUIRE_POSTGRES: "1"
130
+ steps:
131
+ - uses: actions/checkout@v4
132
+ - uses: astral-sh/setup-uv@v5
133
+ with:
134
+ enable-cache: true
135
+ - run: uv python install 3.12
136
+ - run: uv sync --frozen --python 3.12
137
+ - name: Install interlock and AgentGov from main
138
+ run: >-
139
+ uv pip install --python .venv/bin/python
140
+ "interlock-agentgov @ git+https://github.com/crimsondevil0929/agentgov@main"
141
+ "interlock-escrow @ git+https://github.com/crimsondevil0929/interlock@main"
142
+ - name: Type-check the package against them (mypy --strict)
143
+ run: uv run --no-sync mypy src/
144
+ - name: Tests, both stores
145
+ run: uv run --no-sync pytest -v
146
+
147
+ build:
148
+ name: build (wheel + sdist)
149
+ runs-on: ubuntu-latest
150
+ steps:
151
+ - uses: actions/checkout@v4
152
+ - uses: astral-sh/setup-uv@v5
153
+ - run: uv build -o "${{ runner.temp }}/dist" > "${{ runner.temp }}/build.log" 2>&1 || (cat "${{ runner.temp }}/build.log"; exit 1)
154
+ - name: Install the wheel into a bare environment
155
+ run: |
156
+ uv venv "${{ runner.temp }}/bare" --python 3.12
157
+ uv pip install --python "${{ runner.temp }}/bare/bin/python" "${{ runner.temp }}"/dist/*.whl
158
+ "${{ runner.temp }}/bare/bin/fiduciary" --version
159
+ "${{ runner.temp }}/bare/bin/python" -W error -c "import fiduciary.cli, fiduciary.postgres, fiduciary.verify"
@@ -0,0 +1,128 @@
1
+ name: Release
2
+
3
+ # On a version tag: every check of ci.yml, then the wheel and the sdist, built
4
+ # once, published to PyPI by trusted publishing: no API token exists anywhere,
5
+ # PyPI exchanges this workflow's OIDC identity for a short-lived one. Every
6
+ # distribution carries a PEP 740 attestation (PyPI shows and verifies it) and a
7
+ # GitHub build provenance attestation (`gh attestation verify`), and the GitHub
8
+ # release carries a CycloneDX SBOM of everything the package and its extras
9
+ # install: the pipeline of interlock-escrow and interlock-agentgov.
10
+ #
11
+ # Before the first release, register this repository, this workflow
12
+ # (release.yml) and the `pypi` environment as a trusted publisher of
13
+ # interlock-fiduciary on PyPI, and give the environment required reviewers.
14
+
15
+ on:
16
+ push:
17
+ tags: ["v*"]
18
+
19
+ permissions:
20
+ contents: read
21
+
22
+ jobs:
23
+ checks:
24
+ name: every check
25
+ uses: ./.github/workflows/ci.yml
26
+
27
+ build:
28
+ name: build the distributions and their SBOM
29
+ needs: checks
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+
34
+ - name: Install uv
35
+ uses: astral-sh/setup-uv@v5
36
+
37
+ - name: The tag names the version in pyproject.toml and fiduciary.__version__
38
+ run: |
39
+ declared=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
40
+ module=$(python3 -c "import ast; tree = ast.parse(open('src/fiduciary/__init__.py').read()); print(next(n.value.value for n in tree.body if isinstance(n, ast.Assign) and any(getattr(t, 'id', '') == '__version__' for t in n.targets)))")
41
+ if [ "v$declared" != "$GITHUB_REF_NAME" ] || [ "$module" != "$declared" ]; then
42
+ echo "::error::tag $GITHUB_REF_NAME, pyproject.toml $declared, fiduciary.__version__ $module"
43
+ exit 1
44
+ fi
45
+
46
+ - name: Build, failing on packaging warnings
47
+ run: |
48
+ set -o pipefail
49
+ # The log goes outside the checkout: the sdist packs the source tree.
50
+ uv build 2>&1 | tee "${{ runner.temp }}/build.log"
51
+ if grep -i '^warning' "${{ runner.temp }}/build.log"; then
52
+ echo "::error::uv build emitted packaging warnings"
53
+ exit 1
54
+ fi
55
+
56
+ - name: The wheel installs into a bare environment and runs
57
+ run: |
58
+ uv venv "${{ runner.temp }}/bare" --python 3.12
59
+ uv pip install --python "${{ runner.temp }}/bare/bin/python" dist/*.whl
60
+ "${{ runner.temp }}/bare/bin/fiduciary" --version
61
+
62
+ # Every package `pip install 'interlock-fiduciary[...]'` can bring in, at
63
+ # the versions this release was tested with (uv.lock).
64
+ - name: Describe the dependencies (CycloneDX)
65
+ run: |
66
+ mkdir sbom
67
+ uv export --frozen --no-dev --all-extras --no-emit-project --no-hashes \
68
+ --format requirements-txt --output-file sbom/requirements.txt
69
+ uvx --from cyclonedx-bom==7.4.0 cyclonedx-py requirements sbom/requirements.txt \
70
+ --output-format JSON --output-file "sbom/interlock-fiduciary-${GITHUB_REF_NAME#v}.cdx.json"
71
+
72
+ - uses: actions/upload-artifact@v4
73
+ with:
74
+ name: dist
75
+ path: dist/
76
+
77
+ - uses: actions/upload-artifact@v4
78
+ with:
79
+ name: sbom
80
+ path: sbom/
81
+
82
+ publish:
83
+ name: publish to PyPI
84
+ needs: build
85
+ runs-on: ubuntu-latest
86
+ environment:
87
+ name: pypi
88
+ url: https://pypi.org/project/interlock-fiduciary/
89
+ permissions:
90
+ id-token: write # trusted publishing, and signing the attestations
91
+ attestations: write
92
+ steps:
93
+ - uses: actions/download-artifact@v4
94
+ with:
95
+ name: dist
96
+ path: dist/
97
+
98
+ - name: Attest the distributions' build provenance
99
+ uses: actions/attest-build-provenance@v1
100
+ with:
101
+ subject-path: dist/*
102
+
103
+ - name: Publish, with a PEP 740 attestation for each file
104
+ uses: pypa/gh-action-pypi-publish@release/v1
105
+ with:
106
+ attestations: true
107
+
108
+ github-release:
109
+ name: GitHub release, with the SBOM
110
+ needs: publish
111
+ runs-on: ubuntu-latest
112
+ permissions:
113
+ contents: write
114
+ steps:
115
+ - uses: actions/checkout@v4
116
+
117
+ - uses: actions/download-artifact@v4
118
+ with:
119
+ path: assets/
120
+
121
+ - name: Create the release from the tag
122
+ env:
123
+ GH_TOKEN: ${{ github.token }}
124
+ run: |
125
+ prerelease=""
126
+ if [[ "$GITHUB_REF_NAME" =~ (a|b|rc)[0-9]+$ ]]; then prerelease="--prerelease"; fi
127
+ gh release create "$GITHUB_REF_NAME" --verify-tag --generate-notes $prerelease \
128
+ --title "interlock-fiduciary ${GITHUB_REF_NAME#v}" assets/dist/* assets/sbom/*.cdx.json
@@ -0,0 +1,38 @@
1
+ # Python build and bytecode
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv/
11
+
12
+ # Tooling caches
13
+ .pytest_cache/
14
+ .ruff_cache/
15
+ .mypy_cache/
16
+ .coverage
17
+ .coverage.*
18
+ htmlcov/
19
+ .hypothesis/
20
+
21
+ # Logs, such as a build's: the sdist packs whatever is not ignored
22
+ *.log
23
+
24
+ # Stores and keys made by hand while trying the command line
25
+ *.db
26
+ *.db-wal
27
+ *.db-shm
28
+ *.key
29
+
30
+ # OS metadata
31
+ .DS_Store
32
+
33
+ # The live acceptance suite's key, and its runs: never committed, never packed
34
+ .env.benchmark
35
+ live-run/
36
+
37
+ # The soak's runs (scripts/live_stress_test.py --out): never committed, never packed
38
+ soak/