ciris-server 0.4.1__tar.gz → 0.4.2__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 (368) hide show
  1. {ciris_server-0.4.1 → ciris_server-0.4.2}/Cargo.lock +1 -1
  2. {ciris_server-0.4.1 → ciris_server-0.4.2}/Cargo.toml +1 -1
  3. {ciris_server-0.4.1 → ciris_server-0.4.2}/PKG-INFO +1 -1
  4. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/bootstrap.rs +190 -4
  5. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/compose.rs +33 -3
  6. {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/root_bootstrap.rs +244 -2
  7. {ciris_server-0.4.1 → ciris_server-0.4.2}/.cargo/config.toml +0 -0
  8. {ciris_server-0.4.1 → ciris_server-0.4.2}/.github/workflows/bench.yml +0 -0
  9. {ciris_server-0.4.1 → ciris_server-0.4.2}/.github/workflows/ci.yml +0 -0
  10. {ciris_server-0.4.1 → ciris_server-0.4.2}/.github/workflows/conformance.yml +0 -0
  11. {ciris_server-0.4.1 → ciris_server-0.4.2}/.github/workflows/publish-pypi.yml +0 -0
  12. {ciris_server-0.4.1 → ciris_server-0.4.2}/.github/workflows/release.yml +0 -0
  13. {ciris_server-0.4.1 → ciris_server-0.4.2}/.gitignore +0 -0
  14. {ciris_server-0.4.1 → ciris_server-0.4.2}/CHANGELOG.md +0 -0
  15. {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/LENS_TO_SERVER_MIGRATION.md +0 -0
  16. {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/MESH_ANIMATION_BRIEF.md +0 -0
  17. {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/MODERATION_CHILD_SAFETY.md +0 -0
  18. {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/ONE_WHEEL_REEXPORT.md +0 -0
  19. {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/PQC_AV_STREAMING_BENCH.md +0 -0
  20. {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/QA_AGAINST_RUST.md +0 -0
  21. {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/REGISTRY_FOLD_DERISK.md +0 -0
  22. {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/SAFETY_LANDSCAPE.md +0 -0
  23. {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/SERVER_1.0_PLAN.md +0 -0
  24. {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/WATCHLIST_DETECTION.md +0 -0
  25. {ciris_server-0.4.1 → ciris_server-0.4.2}/LICENSE +0 -0
  26. {ciris_server-0.4.1 → ciris_server-0.4.2}/MISSION.md +0 -0
  27. {ciris_server-0.4.1 → ciris_server-0.4.2}/README.md +0 -0
  28. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/.gitignore +0 -0
  29. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/README.md +0 -0
  30. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/build.gradle.kts +0 -0
  31. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/build.gradle.kts +0 -0
  32. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/auth/FederationSigner.kt +0 -0
  33. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/model/federation/Erasure.kt +0 -0
  34. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/model/federation/FederationContent.kt +0 -0
  35. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/model/federation/FederationMetrics.kt +0 -0
  36. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/model/federation/FederationPeer.kt +0 -0
  37. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/model/federation/NodeCode.kt +0 -0
  38. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/model/identity/LocalIdentityAggregate.kt +0 -0
  39. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/net/FabricClient.kt +0 -0
  40. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/net/FabricEndpoints.kt +0 -0
  41. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/platform/PlatformLogger.kt +0 -0
  42. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/ui/screens/ErasureScreen.kt +0 -0
  43. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/ui/screens/IdentityScreen.kt +0 -0
  44. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/viewmodel/ErasureViewModel.kt +0 -0
  45. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/viewmodel/IdentityViewModel.kt +0 -0
  46. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonTest/kotlin/ai/ciris/fabric/IdentitySliceTest.kt +0 -0
  47. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/gradle/wrapper/gradle-wrapper.jar +0 -0
  48. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/gradle/wrapper/gradle-wrapper.properties +0 -0
  49. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/gradle.properties +0 -0
  50. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/gradlew +0 -0
  51. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/gradlew.bat +0 -0
  52. {ciris_server-0.4.1 → ciris_server-0.4.2}/app/settings.gradle.kts +0 -0
  53. {ciris_server-0.4.1 → ciris_server-0.4.2}/bench-site/data.json +0 -0
  54. {ciris_server-0.4.1 → ciris_server-0.4.2}/bench-site/index.html +0 -0
  55. {ciris_server-0.4.1 → ciris_server-0.4.2}/benches/alm_chain.rs +0 -0
  56. {ciris_server-0.4.1 → ciris_server-0.4.2}/benches/pqc_av_streaming.rs +0 -0
  57. {ciris_server-0.4.1 → ciris_server-0.4.2}/benches/replication_ingest.rs +0 -0
  58. {ciris_server-0.4.1 → ciris_server-0.4.2}/benches/stream_fanout.rs +0 -0
  59. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/.github/workflows/bench.yml +0 -0
  60. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/.github/workflows/ci.yml +0 -0
  61. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/.gitignore +0 -0
  62. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/.pre-commit-config.yaml +0 -0
  63. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/Cargo.toml +0 -0
  64. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/FSD/CIRIS_LENS_CORE.md +0 -0
  65. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/FSD/LENS_CORE_V0_5.md +0 -0
  66. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/FSD/OPEN_QUESTIONS.md +0 -0
  67. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/LICENSE +0 -0
  68. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/MISSION.md +0 -0
  69. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/README.md +0 -0
  70. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/benches/aggregate.rs +0 -0
  71. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/benches/canonicalize.rs +0 -0
  72. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/benches/project.rs +0 -0
  73. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/deny.toml +0 -0
  74. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/BENCHMARKS.md +0 -0
  75. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/COHABITATION.md +0 -0
  76. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/PRE_COMMIT.md +0 -0
  77. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/PUBLIC_SCHEMA_CONTRACT.md +0 -0
  78. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/PYPI_PUBLISH.md +0 -0
  79. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/RELEASE_NOTES.md +0 -0
  80. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/STANDARDS_COMPARISON.md +0 -0
  81. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/THREAT_MODEL.md +0 -0
  82. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/extraction/json_path.rs +0 -0
  83. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/extraction/metadata.rs +0 -0
  84. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/extraction/mod.rs +0 -0
  85. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/distilbert_loader.rs +0 -0
  86. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/fields.rs +0 -0
  87. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/mod.rs +0 -0
  88. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/ner.rs +0 -0
  89. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/ort_loader.rs +0 -0
  90. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/proptests.rs +0 -0
  91. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/regex.rs +0 -0
  92. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/walker.rs +0 -0
  93. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/xlm_r_loader.rs +0 -0
  94. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/pyproject.toml +0 -0
  95. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/python/ciris_lens_core/__init__.py +0 -0
  96. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/audit/api.rs +0 -0
  97. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/audit/delegate.rs +0 -0
  98. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/audit/mod.rs +0 -0
  99. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/audit/pyo3.rs +0 -0
  100. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/canonical/ceg_egress.rs +0 -0
  101. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/canonical/mod.rs +0 -0
  102. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capacity/attestation.rs +0 -0
  103. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capacity/mod.rs +0 -0
  104. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capacity/score.rs +0 -0
  105. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/batch.rs +0 -0
  106. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/client.rs +0 -0
  107. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/consent.rs +0 -0
  108. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/correlation.rs +0 -0
  109. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/event.rs +0 -0
  110. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/mod.rs +0 -0
  111. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/partial.rs +0 -0
  112. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/py_engine.rs +0 -0
  113. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/seal.rs +0 -0
  114. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/cohort/declared.rs +0 -0
  115. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/cohort/mod.rs +0 -0
  116. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/cohort/resourcing.rs +0 -0
  117. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/config/egress.rs +0 -0
  118. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/config/mod.rs +0 -0
  119. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/config/node.rs +0 -0
  120. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/config/retention.rs +0 -0
  121. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/config/upstream.rs +0 -0
  122. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/detector/axis_metrics.rs +0 -0
  123. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/detector/coherence_ratchet.rs +0 -0
  124. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/detector/correlated_action.rs +0 -0
  125. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/detector/distributive_access.rs +0 -0
  126. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/detector/manifold.rs +0 -0
  127. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/detector/mod.rs +0 -0
  128. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/extract/mod.rs +0 -0
  129. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/extract/projection.rs +0 -0
  130. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/ffi/mod.rs +0 -0
  131. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/ffi/pyo3.rs +0 -0
  132. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/lib.rs +0 -0
  133. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/observability/mod.rs +0 -0
  134. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/pipeline/lifecycle.rs +0 -0
  135. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/pipeline/mod.rs +0 -0
  136. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/retention/eviction.rs +0 -0
  137. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/retention/mod.rs +0 -0
  138. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/retention/summary.rs +0 -0
  139. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/role/handler.rs +0 -0
  140. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/role/mod.rs +0 -0
  141. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/role/node.rs +0 -0
  142. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/role/relay.rs +0 -0
  143. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/role/ret_relay.rs +0 -0
  144. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scores/aggregate.rs +0 -0
  145. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scores/mod.rs +0 -0
  146. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scores/oracle.rs +0 -0
  147. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/assembly.rs +0 -0
  148. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/axis_calibration.rs +0 -0
  149. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/calibration.rs +0 -0
  150. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/capacity.rs +0 -0
  151. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/mod.rs +0 -0
  152. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/n_eff.rs +0 -0
  153. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/result.rs +0 -0
  154. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/signing/event.rs +0 -0
  155. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/signing/mod.rs +0 -0
  156. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/wire/mod.rs +0 -0
  157. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/wire/signer.rs +0 -0
  158. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/tests/parity/README.md +0 -0
  159. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/tests/parity/canonical_fixtures.json +0 -0
  160. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/tests/parity/generate_canonical_fixtures.py +0 -0
  161. {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/tests/sovereign_rlib.rs +0 -0
  162. {ciris_server-0.4.1 → ciris_server-0.4.2}/pyproject.toml +0 -0
  163. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/README.md +0 -0
  164. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/README.md +0 -0
  165. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/REFACTORING_SUMMARY.md +0 -0
  166. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/__init__.py +0 -0
  167. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/auth_store.py +0 -0
  168. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/client.py +0 -0
  169. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/examples/__init__.py +0 -0
  170. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/examples/agent_interaction.py +0 -0
  171. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/examples/unified_telemetry_examples.py +0 -0
  172. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/examples/wa_example.py +0 -0
  173. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/exceptions.py +0 -0
  174. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/model_types.py +0 -0
  175. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/models.py +0 -0
  176. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/pagination.py +0 -0
  177. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/rate_limiter.py +0 -0
  178. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/CONFIG_CLIENT.md +0 -0
  179. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/WA_CLIENT_GUIDE.md +0 -0
  180. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/__init__.py +0 -0
  181. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/agent.py +0 -0
  182. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/audit.py +0 -0
  183. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/auth.py +0 -0
  184. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/billing.py +0 -0
  185. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/config.py +0 -0
  186. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/consent.py +0 -0
  187. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/emergency.py +0 -0
  188. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/jobs.py +0 -0
  189. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/memory.py +0 -0
  190. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/setup.py +0 -0
  191. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/system.py +0 -0
  192. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/telemetry.py +0 -0
  193. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/wa.py +0 -0
  194. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/websocket.py +0 -0
  195. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/setup.py +0 -0
  196. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/telemetry_models.py +0 -0
  197. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/telemetry_responses.py +0 -0
  198. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/transport.py +0 -0
  199. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/websocket.py +0 -0
  200. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/fabric_results.json +0 -0
  201. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/BILLING_QA_README.md +0 -0
  202. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/CLAUDE.md +0 -0
  203. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/README.md +0 -0
  204. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/SDK_MODULE_INTEGRATION.md +0 -0
  205. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/__init__.py +0 -0
  206. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/__main__.py +0 -0
  207. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/config.py +0 -0
  208. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/mcp_test_server.py +0 -0
  209. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/CLAUDE.md +0 -0
  210. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/MULTI_OCCURRENCE_README.md +0 -0
  211. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/__init__.py +0 -0
  212. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/_module_metadata.py +0 -0
  213. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/accord_metrics_tests.py +0 -0
  214. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/accord_tests.py +0 -0
  215. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/adapter_autoload_tests.py +0 -0
  216. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/adapter_availability_tests.py +0 -0
  217. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/adapter_config_tests.py +0 -0
  218. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/adapter_manifest_tests.py +0 -0
  219. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/agent_mode_tests.py +0 -0
  220. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/air_tests.py +0 -0
  221. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/api_tests.py +0 -0
  222. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/base_test_module.py +0 -0
  223. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/billing_integration_tests.py +0 -0
  224. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/billing_tests.py +0 -0
  225. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/cirisnode_tests.py +0 -0
  226. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/cognitive_state_api_tests.py +0 -0
  227. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/comprehensive_api_tests.py +0 -0
  228. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/comprehensive_single_step_tests.py +0 -0
  229. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/consent_tests.py +0 -0
  230. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/context_enrichment_tests.py +0 -0
  231. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/deferral_taxonomy_tests.py +0 -0
  232. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/deferral_tests.py +0 -0
  233. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/degraded_mode_tests.py +0 -0
  234. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/desktop_viz.py +0 -0
  235. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/dream_live_tests.py +0 -0
  236. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/dsar_multi_source_tests.py +0 -0
  237. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/dsar_tests.py +0 -0
  238. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/dsar_ticket_workflow_tests.py +0 -0
  239. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/filter_test_helper.py +0 -0
  240. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/filter_tests.py +0 -0
  241. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/handler_tests.py +0 -0
  242. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/he300_benchmark_tests.py +0 -0
  243. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/homeassistant_agentic_tests.py +0 -0
  244. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/hosted_tools_tests.py +0 -0
  245. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/identity_update_tests.py +0 -0
  246. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/l4_attestation_tests.py +0 -0
  247. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/licensed_agent_tests.py +0 -0
  248. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mcp_tests.py +0 -0
  249. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/memory_benchmark_tests.py +0 -0
  250. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/message_id_debug_test.py +0 -0
  251. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/__init__.py +0 -0
  252. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/__main__.py +0 -0
  253. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/adb_helper.py +0 -0
  254. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/device_helper.py +0 -0
  255. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/first_time_licensed_agent.py +0 -0
  256. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios/__init__.py +0 -0
  257. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios/build_helper.py +0 -0
  258. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios/idevice_helper.py +0 -0
  259. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios/ios_ui_automator.py +0 -0
  260. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios/vision_helper.py +0 -0
  261. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios/xcrun_helper.py +0 -0
  262. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios_physical_test_cases.py +0 -0
  263. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios_test_cases.py +0 -0
  264. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/portal_registration.py +0 -0
  265. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/test_cases.py +0 -0
  266. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/test_runner.py +0 -0
  267. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ui_automator.py +0 -0
  268. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/verify_trust_tests.py +0 -0
  269. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/model_eval_tests.py +0 -0
  270. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/multi_occurrence_tests.py +0 -0
  271. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/parallel_locales_tests.py +0 -0
  272. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/partnership_tests.py +0 -0
  273. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/play_live_tests.py +0 -0
  274. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/reddit_tests.py +0 -0
  275. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/safety_battery.py +0 -0
  276. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/safety_interpret.py +0 -0
  277. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/sdk_tests.py +0 -0
  278. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/secrets_encryption_tests.py +0 -0
  279. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/setup_e2e_tests.py +0 -0
  280. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/setup_tests.py +0 -0
  281. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/simple_single_step_tests.py +0 -0
  282. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/solitude_live_tests.py +0 -0
  283. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/sql_external_data_tests.py +0 -0
  284. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/state_transition_tests.py +0 -0
  285. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/streaming_verification.py +0 -0
  286. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/system_messages_tests.py +0 -0
  287. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/utility_adapters_tests.py +0 -0
  288. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/vision_tests.py +0 -0
  289. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/wallet_tests.py +0 -0
  290. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/CLAUDE.md +0 -0
  291. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/__init__.py +0 -0
  292. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/__main__.py +0 -0
  293. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/browser_helper.py +0 -0
  294. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/desktop_app_helper.py +0 -0
  295. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/desktop_test.py +0 -0
  296. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/federation_screen_tags.py +0 -0
  297. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/federation_walk_report.py +0 -0
  298. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/federation_walk_test.py +0 -0
  299. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/licensed_agent_flow.py +0 -0
  300. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/server_manager.py +0 -0
  301. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/test_cases.py +0 -0
  302. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/test_federation_walk_unit.py +0 -0
  303. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/test_runner.py +0 -0
  304. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_api_test.py +0 -0
  305. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports/qa_report_20250825_073205.json +0 -0
  306. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports/qa_report_20250825_153259.json +0 -0
  307. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports/qa_report_20250825_154628.json +0 -0
  308. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports/qa_report_20250825_154817.json +0 -0
  309. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports/qa_report_20250825_155917.json +0 -0
  310. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports_comprehensive/qa_report_20250825_075504.json +0 -0
  311. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports_final/qa_report_20250825_092420.json +0 -0
  312. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports_test/qa_report_20250825_092452.json +0 -0
  313. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_scripts/README.md +0 -0
  314. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_scripts/qa_api_test.py +0 -0
  315. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_scripts/qa_v1.0_sdk_comprehensive.py +0 -0
  316. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_scripts/qa_v1.0_unhealthy_services_check.py +0 -0
  317. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_test.py +0 -0
  318. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_test_sdk.py +0 -0
  319. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/requirements.txt +0 -0
  320. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/runner.py +0 -0
  321. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/scripts/__init__.py +0 -0
  322. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/scripts/he300_stream_debug.py +0 -0
  323. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/server.py +0 -0
  324. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/staged_env.py +0 -0
  325. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/status_tracker.py +0 -0
  326. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/test_data/dsar_multi_source_privacy_schema.yaml +0 -0
  327. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/test_data/qa_test.db +0 -0
  328. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/test_data/qa_test_privacy_schema.yaml +0 -0
  329. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/test_data/sql_config.json +0 -0
  330. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/run_fabric.py +0 -0
  331. {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/seed_wa_cert.py +0 -0
  332. {ciris_server-0.4.1 → ciris_server-0.4.2}/rust-toolchain.toml +0 -0
  333. {ciris_server-0.4.1 → ciris_server-0.4.2}/scripts/bench_report.py +0 -0
  334. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/ABSORPTION.md +0 -0
  335. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/api_keys.rs +0 -0
  336. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/attestation.rs +0 -0
  337. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/consent.rs +0 -0
  338. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/device_auth.rs +0 -0
  339. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/erasure.rs +0 -0
  340. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/gate.rs +0 -0
  341. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/mod.rs +0 -0
  342. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/oauth.rs +0 -0
  343. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/roles.rs +0 -0
  344. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/self_login.rs +0 -0
  345. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/session.rs +0 -0
  346. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/store.rs +0 -0
  347. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/verify.rs +0 -0
  348. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/benchmarks/mod.rs +0 -0
  349. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/benchmarks/scoreboard.rs +0 -0
  350. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/config.rs +0 -0
  351. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/federation_admin.rs +0 -0
  352. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/federation_nodecode.rs +0 -0
  353. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/import.rs +0 -0
  354. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/lib.rs +0 -0
  355. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/main.rs +0 -0
  356. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/nodecode.rs +0 -0
  357. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/peer.rs +0 -0
  358. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/quorum.rs +0 -0
  359. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/scorer/n_eff.rs +0 -0
  360. {ciris_server-0.4.1 → ciris_server-0.4.2}/src/scorer.rs +0 -0
  361. {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/alm_chain.rs +0 -0
  362. {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/capacity_scorer.rs +0 -0
  363. {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/chaos_mesh.rs +0 -0
  364. {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/federation_admin.rs +0 -0
  365. {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/nodecode.rs +0 -0
  366. {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/noise_floor.rs +0 -0
  367. {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/peer_replication.rs +0 -0
  368. {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/replication.rs +0 -0
@@ -994,7 +994,7 @@ dependencies = [
994
994
 
995
995
  [[package]]
996
996
  name = "ciris-server"
997
- version = "0.4.1"
997
+ version = "0.4.2"
998
998
  dependencies = [
999
999
  "anyhow",
1000
1000
  "async-trait",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "ciris-server"
3
- version = "0.4.1" # 0.1 = lens-only (agent ~2.9.7); 0.3 = +auth subsystem / one-wheel; 0.4 = +federation peering/identity (capacity scoring, consent:replication, register_federation_key gate, root-bootstrap, NodeCode); 0.4.1 = self-attested first-run root claim (fresh-founder hybrid PoP, #887); 0.5 = +registry (~2.9.8); 1.0 = +node (~2.9.10). Patch forward on breakage.
3
+ version = "0.4.2" # 0.1 = lens-only (agent ~2.9.7); 0.3 = +auth subsystem / one-wheel; 0.4 = +federation peering/identity (capacity scoring, consent:replication, register_federation_key gate, root-bootstrap, NodeCode); 0.4.1 = self-attested first-run root claim (fresh-founder hybrid PoP, #887); 0.4.2 = one-time claim PIN on fresh boot (operator-presence gate); 0.5 = +registry (~2.9.8); 1.0 = +node (~2.9.10). Patch forward on breakage.
4
4
  edition = "2021"
5
5
  # MSRV floor is set by the substrate: ciris-verify v5.2.0 requires 1.86
6
6
  # (persist v6.0.1 is 1.83). Build with the higher of the two.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ciris-server
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  License-File: LICENSE
5
5
  Summary: The fabric node — the federation's headless cohabitation runtime (registry + lens [+ node] over one shared substrate), as the abi3 wheel CIRISAgent consumes. agent = fabric node + brain.
6
6
  Author-email: Eric Moore <eric@ciris.ai>
@@ -45,7 +45,7 @@
45
45
  //! converts, so an operator can drop the agent's `seed/root_pub.json` in verbatim.
46
46
 
47
47
  use std::path::Path;
48
- use std::sync::Arc;
48
+ use std::sync::{Arc, Mutex};
49
49
 
50
50
  use axum::extract::State;
51
51
  use axum::http::{HeaderMap, StatusCode};
@@ -57,10 +57,18 @@ use ciris_persist::verify::canonical::ceg_produce_canonicalize;
57
57
  use ciris_persist::wa_cert::{TokenType, WaCert, WaRole};
58
58
  use serde::{Deserialize, Serialize};
59
59
  use sha2::{Digest, Sha256};
60
+ use subtle::ConstantTimeEq;
60
61
 
61
62
  use super::store;
62
63
  use super::verify;
63
64
 
65
+ /// Env: an OPTIONAL filesystem path the one-time claim PIN is ALSO written to on a
66
+ /// fresh (unclaimed) boot, for headless ops that cannot scrape the console log. The
67
+ /// default is console/log ONLY — the PIN is the operator-presence secret and is
68
+ /// NEVER served over any HTTP route. When set, the PIN file is written `0600` and
69
+ /// the operator is responsible for removing it after the claim.
70
+ pub const ENV_CLAIM_PIN_FILE: &str = "CIRIS_CLAIM_PIN_FILE";
71
+
64
72
  /// Env: a filesystem path to a `root_pub.json`-shaped seed (the agent's shape).
65
73
  pub const ENV_SEED_PATH: &str = "CIRIS_ROOT_SEED_PATH";
66
74
  /// Env: the ROOT public key (base64/base64url Ed25519) — the seed-less inline form.
@@ -439,6 +447,81 @@ fn root_wa_id_for_identity(identity_key_id: &str) -> String {
439
447
  format!("wa-root-{identity_key_id}")
440
448
  }
441
449
 
450
+ // ─── One-time CLAIM PIN — the operator-presence secret (console-only) ─────────
451
+
452
+ /// Crockford base32 alphabet (excludes I, L, O, U to stay operator-typable —
453
+ /// no visual confusion with 1/0 and no accidental words).
454
+ const CLAIM_PIN_ALPHABET: &[u8; 32] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
455
+ /// Number of PIN characters (rendered as two dash-separated groups of 4).
456
+ const CLAIM_PIN_LEN: usize = 8;
457
+
458
+ /// Generate a cryptographically-random, operator-typable one-time claim PIN —
459
+ /// [`CLAIM_PIN_LEN`] Crockford-base32 chars rendered `XXXX-XXXX`.
460
+ ///
461
+ /// This is the **operator-presence secret** that gates the first-run ownership
462
+ /// claim. It closes the hole that the NodeCode alone is a PUBLIC, freely-shareable
463
+ /// handle (served by `GET /v1/federation/node-code`): knowing the NodeCode is not
464
+ /// enough — the claimant must ALSO present this PIN, which is printed ONLY to the
465
+ /// node's console/log on a fresh boot and NEVER exposed via any HTTP route.
466
+ ///
467
+ /// Sourced from `getrandom` (the OS CSPRNG). Rejection-free: the alphabet is
468
+ /// exactly 32 chars, so each random byte maps to one symbol via a 5-bit mask with
469
+ /// zero modulo bias.
470
+ pub fn generate_claim_pin() -> String {
471
+ let mut bytes = [0u8; CLAIM_PIN_LEN];
472
+ getrandom::fill(&mut bytes).expect("OS CSPRNG (getrandom) for the one-time claim PIN");
473
+ let mut chars: Vec<char> = bytes
474
+ .iter()
475
+ .map(|b| CLAIM_PIN_ALPHABET[(b & 0x1F) as usize] as char)
476
+ .collect();
477
+ // Render as two dash-separated groups of 4 (XXXX-XXXX) for typability.
478
+ chars.insert(4, '-');
479
+ chars.into_iter().collect()
480
+ }
481
+
482
+ /// Print the unmissable "OWNERSHIP UNCLAIMED" banner at startup — the NodeCode (a
483
+ /// PUBLIC handle) PLUS the one-time claim PIN (the console-only operator-presence
484
+ /// secret). Optionally ALSO writes the PIN to [`ENV_CLAIM_PIN_FILE`] (`0600`) for
485
+ /// headless ops. The PIN is NEVER served over HTTP.
486
+ pub fn announce_ownership_unclaimed(node_code: &str, claim_pin: &str) {
487
+ tracing::warn!(
488
+ "\n\
489
+ ╔══════════════════════════════════════════════════════════════════════╗\n\
490
+ ║ OWNERSHIP UNCLAIMED — this node has no ROOT owner yet. ║\n\
491
+ ║ ║\n\
492
+ ║ To claim ownership (POST /v1/setup/root), present BOTH: ║\n\
493
+ ║ ║\n\
494
+ ║ NodeCode : {node_code}\n\
495
+ ║ CLAIM PIN: {claim_pin} (one-time; console-only — NEVER over HTTP)\n\
496
+ ║ ║\n\
497
+ ║ The NodeCode is a PUBLIC handle (GET /v1/federation/node-code). ║\n\
498
+ ║ The PIN is the operator-presence secret: it is printed ONLY here ║\n\
499
+ ║ and is consumed on the first successful claim. ║\n\
500
+ ╚══════════════════════════════════════════════════════════════════════╝"
501
+ );
502
+
503
+ // Optional headless-ops sink: write the PIN to a 0600 file when configured.
504
+ if let Ok(path) = std::env::var(ENV_CLAIM_PIN_FILE) {
505
+ let path = path.trim();
506
+ if !path.is_empty() {
507
+ match std::fs::write(path, format!("{claim_pin}\n")) {
508
+ Ok(()) => {
509
+ #[cfg(unix)]
510
+ {
511
+ use std::os::unix::fs::PermissionsExt;
512
+ let _ =
513
+ std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600));
514
+ }
515
+ tracing::info!(pin_file = %path, "one-time claim PIN ALSO written to file (0600; remove after claim)");
516
+ }
517
+ Err(e) => {
518
+ tracing::warn!(pin_file = %path, error = %e, "could not write claim PIN file (PIN remains available on the console)")
519
+ }
520
+ }
521
+ }
522
+ }
523
+ }
524
+
442
525
  // ─── POST /v1/setup/root — the first-run root-claim route ────────────────────
443
526
 
444
527
  #[derive(Clone)]
@@ -451,6 +534,12 @@ struct SetupState {
451
534
  /// THIS node's raw Ed25519 pubkey (base64) — the second half of the NodeCode
452
535
  /// identity pin.
453
536
  node_pubkey_ed25519_base64: String,
537
+ /// The one-time claim PIN minted on a fresh (unclaimed) boot — the
538
+ /// operator-presence secret. `Some(pin)` while unclaimed-and-armed; set to
539
+ /// `None` once a ROOT exists at boot (already claimed → no PIN), or CLEARED on
540
+ /// the first successful claim (consume-once; no replay). Held behind a `Mutex`
541
+ /// so the claim handler can take it. NEVER reachable via any HTTP route.
542
+ claim_pin: Arc<Mutex<Option<String>>>,
454
543
  }
455
544
 
456
545
  /// The FRESH founder's self-attested hybrid identity (the claimant's OWN keys).
@@ -503,6 +592,14 @@ struct SetupRootRequest {
503
592
  /// NOT the federation directory.
504
593
  #[serde(default)]
505
594
  founder: Option<Founder>,
595
+ /// The one-time CLAIM PIN printed to THIS node's console/log on a fresh boot —
596
+ /// the operator-presence secret (CONSOLE-ONLY; never served over HTTP). It is
597
+ /// verified (constant-time) against the boot PIN before ROOT is bound, and
598
+ /// consumed on success. It rides INSIDE the signed body, so it is
599
+ /// signature-bound (it cannot be stripped/swapped without breaking the hybrid
600
+ /// PoP signature). REQUIRED.
601
+ #[serde(default)]
602
+ claim_pin: Option<String>,
506
603
  }
507
604
 
508
605
  #[derive(Debug, Serialize)]
@@ -557,11 +654,69 @@ fn verify_node_code_pin(st: &SetupState, req: &SetupRootRequest) -> Option<Respo
557
654
  None
558
655
  }
559
656
 
657
+ /// Verify the one-time CLAIM PIN in the signed claim body against the boot PIN
658
+ /// held in [`SetupState`], using a CONSTANT-TIME comparison (no early-return on
659
+ /// first-byte mismatch, no length-leaking shortcut beyond the unavoidable
660
+ /// length-equality flag). Returns `None` on a match; `Some(401)` when the PIN is
661
+ /// absent, when no boot PIN is armed (already-claimed / mis-bootstrapped node), or
662
+ /// when it does not match. The PIN is the operator-presence secret: it is printed
663
+ /// ONLY to the console and is NEVER served over any HTTP route.
664
+ fn verify_claim_pin(st: &SetupState, req: &SetupRootRequest) -> Option<Response> {
665
+ // The armed boot PIN. A missing/cleared PIN means the route must not admit a
666
+ // claim (node already claimed at boot, or PIN already consumed) → reject.
667
+ let armed = match st.claim_pin.lock() {
668
+ Ok(g) => g.clone(),
669
+ Err(_) => None,
670
+ };
671
+ let Some(expected) = armed else {
672
+ return Some(err(
673
+ StatusCode::UNAUTHORIZED,
674
+ "this node is not armed for a first-run claim (no one-time PIN) — \
675
+ ownership may already be claimed",
676
+ ));
677
+ };
678
+
679
+ let Some(supplied) = req.claim_pin.as_deref() else {
680
+ return Some(err(
681
+ StatusCode::UNAUTHORIZED,
682
+ "first-run ROOT claim must carry `claim_pin` (the one-time PIN printed \
683
+ on this node's console at boot)",
684
+ ));
685
+ };
686
+
687
+ // Constant-time equality over the raw bytes. `subtle::ConstantTimeEq` requires
688
+ // equal-length slices to compare byte-by-byte; a length difference is folded in
689
+ // WITHOUT an early return so the path does not short-circuit on first mismatch.
690
+ let a = supplied.as_bytes();
691
+ let b = expected.as_bytes();
692
+ let len_ok = a.len() == b.len();
693
+ // Pad/truncate the supplied bytes to the expected length so `ct_eq` always runs
694
+ // over `expected.len()` bytes (a mismatched length still does full work, then
695
+ // the `len_ok` flag forces the result false).
696
+ let mut padded = vec![0u8; b.len()];
697
+ for (i, slot) in padded.iter_mut().enumerate() {
698
+ *slot = if i < a.len() { a[i] } else { 0 };
699
+ }
700
+ let bytes_eq: bool = padded.ct_eq(b).into();
701
+ if len_ok && bytes_eq {
702
+ return None;
703
+ }
704
+ Some(err(StatusCode::UNAUTHORIZED, "invalid one-time claim PIN"))
705
+ }
706
+
560
707
  /// `POST /v1/setup/root` — first-time-setup ROOT claim for a FRESH node with no
561
708
  /// baked seed.
562
709
  ///
563
- /// # Security model — three independent gates
710
+ /// # Security model — four independent gates
564
711
  ///
712
+ /// 0. **One-time CLAIM PIN (operator-presence)** — the claim MUST carry the
713
+ /// `claim_pin` printed to THIS node's console/log on a fresh boot, verified
714
+ /// CONSTANT-TIME against the boot PIN held in [`SetupState`]. The PIN is NEVER
715
+ /// served over any HTTP route, so it proves the claimant has operator-level
716
+ /// access to the node's console — closing the hole that the NodeCode alone is a
717
+ /// PUBLIC, freely-shareable handle. A wrong/missing PIN → `401` (no ROOT). On
718
+ /// success the PIN is CONSUMED (cleared from state) so it cannot be replayed.
719
+ /// The PIN rides inside the signed body, so it is signature-bound.
565
720
  /// 1. **First-run only** — allowed ONLY when no ROOT WaCert exists; after a ROOT
566
721
  /// exists the route returns `409 Conflict` (no silent re-claim).
567
722
  /// 2. **NodeCode identity-pin (CEG §0.10)** — the claim MUST carry THIS node's
@@ -601,9 +756,13 @@ fn verify_node_code_pin(st: &SetupState, req: &SetupRootRequest) -> Option<Respo
601
756
  /// "key_id": "...",
602
757
  /// "ed25519_pubkey_b64": "...",
603
758
  /// "ml_dsa_65_pubkey_b64": "..."
604
- /// }
759
+ /// },
760
+ /// "claim_pin": "XXXX-XXXX"
605
761
  /// }
606
762
  /// ```
763
+ /// `claim_pin` is the one-time PIN printed to the node's console on a fresh boot
764
+ /// (console-only; NEVER served over HTTP). It is inside the signed body, so the
765
+ /// signature binds it.
607
766
  /// (`node_code` may instead be supplied as the decoded `key_id` +
608
767
  /// `pubkey_ed25519_base64` pair at the top level — the node-pin half, distinct
609
768
  /// from the `founder` object.) The signature MUST cover the body bytes verbatim.
@@ -654,6 +813,17 @@ async fn setup_root(
654
813
  return resp;
655
814
  }
656
815
 
816
+ // (0/operator-presence) One-time CLAIM PIN. The claim must carry the PIN that
817
+ // was printed to THIS node's console/log on a fresh boot — verified
818
+ // CONSTANT-TIME against the boot PIN. The PIN is NEVER served over HTTP, so
819
+ // possessing it proves operator-level console access, closing the hole that
820
+ // the NodeCode alone is a freely-shareable PUBLIC handle. A wrong/missing
821
+ // PIN → 401 (no ROOT minted). The PIN is signature-bound (inside the signed
822
+ // body). It is NOT consumed here — only on a fully-successful claim below.
823
+ if let Some(resp) = verify_claim_pin(&st, &req) {
824
+ return resp;
825
+ }
826
+
657
827
  // (3) Self-attested hybrid proof-of-possession. A FRESH founder is not in the
658
828
  // directory, so we verify the body signature against the founder's OWN
659
829
  // pubkeys (NOT the directory) under Strict policy (both sigs required).
@@ -772,11 +942,17 @@ async fn setup_root(
772
942
  };
773
943
  match store::upsert(&st.engine, cert).await {
774
944
  Ok(()) => {
945
+ // Consume the one-time PIN: clear it so it can never be replayed. (The
946
+ // route is also 409-closed now that a ROOT exists — this is defence in
947
+ // depth + makes the consumed-once invariant directly assertable.)
948
+ if let Ok(mut guard) = st.claim_pin.lock() {
949
+ *guard = None;
950
+ }
775
951
  tracing::info!(
776
952
  identity = %founder.key_id,
777
953
  wa_id = %wa_id,
778
954
  "first-run ROOT claim: self-attested founder registered + bound as ROOT \
779
- (founder → SYSTEM_ADMIN)"
955
+ (founder → SYSTEM_ADMIN); one-time claim PIN consumed"
780
956
  );
781
957
  (
782
958
  StatusCode::CREATED,
@@ -865,11 +1041,20 @@ async fn register_founder_key(
865
1041
  /// `node_key_id` + `node_pubkey_ed25519_base64` are THIS node's steward identity
866
1042
  /// (the same `key_id` + raw Ed25519 pubkey carried on its NodeCode, CEG §0.10);
867
1043
  /// the first-run claim is identity-pinned to them (see [`setup_root`]).
1044
+ ///
1045
+ /// `claim_pin` is the one-time operator-presence secret: `Some(pin)` arms the
1046
+ /// route for a fresh (unclaimed) node — the boot path mints it via
1047
+ /// [`generate_claim_pin`], prints it via [`announce_ownership_unclaimed`], and the
1048
+ /// claim is gated on it (constant-time, consumed on success). `None` (an
1049
+ /// already-claimed node) leaves the route un-armed — every claim attempt is `401`
1050
+ /// (and `409` once the first-run check sees the existing ROOT). The PIN is held in
1051
+ /// state ONLY; it is NEVER served over any HTTP route.
868
1052
  pub fn router(
869
1053
  engine: Arc<Engine>,
870
1054
  policy: HybridPolicy,
871
1055
  node_key_id: String,
872
1056
  node_pubkey_ed25519_base64: String,
1057
+ claim_pin: Option<String>,
873
1058
  ) -> Router {
874
1059
  Router::new()
875
1060
  .route("/v1/setup/root", axum::routing::post(setup_root))
@@ -878,6 +1063,7 @@ pub fn router(
878
1063
  policy,
879
1064
  node_key_id,
880
1065
  node_pubkey_ed25519_base64,
1066
+ claim_pin: Arc::new(Mutex::new(claim_pin)),
881
1067
  })
882
1068
  }
883
1069
 
@@ -84,11 +84,14 @@ pub async fn serve(cfg: ServerConfig) -> Result<()> {
84
84
  // POST /v1/setup/root claim then lets the founder claim ROOT on a fresh node.
85
85
  // Either way the founder's identity becomes WaRole::Root → UserRole::SystemAdmin,
86
86
  // which is what the owner-gated POST /v1/federation/peering requires. Idempotent.
87
- match crate::auth::bootstrap::bootstrap_if_needed(&engine).await {
88
- Ok(outcome) => tracing::info!(?outcome, "root-user bootstrap evaluated"),
87
+ let bootstrap_outcome = match crate::auth::bootstrap::bootstrap_if_needed(&engine).await {
88
+ Ok(outcome) => {
89
+ tracing::info!(?outcome, "root-user bootstrap evaluated");
90
+ outcome
91
+ }
89
92
  // A bad seed must not silently downgrade owner-claim to "open forever"; fail boot.
90
93
  Err(e) => return Err(anyhow::anyhow!("root-user bootstrap: {e}")),
91
- }
94
+ };
92
95
 
93
96
  // The node's OWN self-signed SignedKeyRecord as JSON — built ONCE at boot
94
97
  // (stable for the node's lifetime), served verbatim by
@@ -106,6 +109,32 @@ pub async fn serve(cfg: ServerConfig) -> Result<()> {
106
109
  let node_code_response_json = crate::federation_nodecode::render_response_json(&node_code)
107
110
  .map_err(|e| anyhow::anyhow!("render this node's NodeCode response: {e}"))?;
108
111
 
112
+ // ── One-time CLAIM PIN — the operator-presence secret for the first-run
113
+ // ownership claim (CIRISServer first-run-PIN). On a FRESH, UNCLAIMED boot
114
+ // (no ROOT WaCert, no seed → BootstrapOutcome::NoSeedAvailable) mint a
115
+ // cryptographically-random, operator-typable PIN, print it in an unmissable
116
+ // banner ALONGSIDE the NodeCode, and arm the POST /v1/setup/root route with
117
+ // it. If a ROOT already exists (AlreadyBootstrapped / SeededRoot) NO PIN is
118
+ // minted — the route is 409-closed anyway. The PIN closes the hole that the
119
+ // NodeCode alone is a freely-shareable PUBLIC handle; it is printed ONLY to
120
+ // the console/log (optionally CIRIS_CLAIM_PIN_FILE) and is NEVER served over
121
+ // any HTTP route.
122
+ let claim_pin: Option<String> = if bootstrap_outcome
123
+ == crate::auth::bootstrap::BootstrapOutcome::NoSeedAvailable
124
+ {
125
+ let pin = crate::auth::bootstrap::generate_claim_pin();
126
+ let node_code_str = crate::nodecode::encode(&node_code).map_err(|e| {
127
+ anyhow::anyhow!("encode this node's NodeCode for the claim banner: {e}")
128
+ })?;
129
+ crate::auth::bootstrap::announce_ownership_unclaimed(&node_code_str, &pin);
130
+ Some(pin)
131
+ } else {
132
+ tracing::info!(
133
+ "node already has a ROOT owner — no first-run claim PIN minted (setup/root is closed)"
134
+ );
135
+ None
136
+ };
137
+
109
138
  // ── ONE shared Reticulum edge runtime — the node's single federation
110
139
  // transport identity. From here the node IS a Reticulum node. ───────────
111
140
  let edge = build_edge(&engine, &cfg, Arc::clone(&signer), Arc::clone(&pqc)).await?;
@@ -184,6 +213,7 @@ pub async fn serve(cfg: ServerConfig) -> Result<()> {
184
213
  strict,
185
214
  node_code.key_id.clone(),
186
215
  node_code.pubkey_ed25519_base64.clone(),
216
+ claim_pin.clone(),
187
217
  ))
188
218
  // sessions/tokens: login / logout / me / refresh / owner-hint
189
219
  .merge(crate::auth::session::router(Arc::clone(&engine)))
@@ -126,14 +126,30 @@ fn founder_signer(key_id: &str) -> LocalSigner {
126
126
  )
127
127
  }
128
128
 
129
+ /// A known, fixed one-time claim PIN the tests arm the router with (the boot path
130
+ /// mints a random one; here we pin a value so a test can supply the correct PIN).
131
+ const TEST_CLAIM_PIN: &str = "TEST-PIN1";
132
+
129
133
  /// Serve the bootstrap (setup) router on an ephemeral port; return its base URL.
130
- /// Pins the router to the node's identity (`key_id` + its Ed25519 pubkey).
134
+ /// Pins the router to the node's identity (`key_id` + its Ed25519 pubkey) and arms
135
+ /// it with the known [`TEST_CLAIM_PIN`].
131
136
  async fn serve(engine: Arc<Engine>, node_key_id: &str) -> (String, tokio::task::JoinHandle<()>) {
137
+ serve_with_pin(engine, node_key_id, Some(TEST_CLAIM_PIN.to_string())).await
138
+ }
139
+
140
+ /// Serve the bootstrap router armed with an EXPLICIT claim PIN (or `None` for an
141
+ /// un-armed/already-claimed node).
142
+ async fn serve_with_pin(
143
+ engine: Arc<Engine>,
144
+ node_key_id: &str,
145
+ claim_pin: Option<String>,
146
+ ) -> (String, tokio::task::JoinHandle<()>) {
132
147
  let app = bootstrap::router(
133
148
  engine,
134
149
  HybridPolicy::Strict,
135
150
  node_key_id.to_string(),
136
151
  node_pubkey_b64(),
152
+ claim_pin,
137
153
  );
138
154
  let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
139
155
  .await
@@ -257,7 +273,9 @@ fn founder_claim_body(node_key_id: &str, founder_key_id: &str) -> serde_json::Va
257
273
  "key_id": founder_key_id,
258
274
  // Pubkeys are filled in by the caller after signing (they come off the
259
275
  // HybridSignature), so this placeholder is replaced below.
260
- }
276
+ },
277
+ // The one-time operator-presence PIN (signature-bound, inside the body).
278
+ "claim_pin": TEST_CLAIM_PIN,
261
279
  })
262
280
  }
263
281
 
@@ -509,6 +527,230 @@ async fn first_run_setup_root_requires_a_pin() {
509
527
  );
510
528
  }
511
529
 
530
+ // ─── one-time CLAIM PIN — the operator-presence secret gate ──────────────────
531
+
532
+ /// Helper: build a fully-signed first-run claim body for `founder_key_id` pinning
533
+ /// `node_key_id`, with an explicit `claim_pin`. Returns the signed body bytes +
534
+ /// the two signature headers (base64).
535
+ async fn signed_claim_with_pin(
536
+ node_key_id: &str,
537
+ founder_key_id: &str,
538
+ claim_pin: Option<&str>,
539
+ ) -> (Vec<u8>, String, String) {
540
+ let fsigner = founder_signer(founder_key_id);
541
+ let probe = fsigner.sign_hybrid(b"probe").await.expect("probe sign");
542
+ let mut claim = serde_json::json!({
543
+ "node_code": node_code_string(node_key_id),
544
+ "founder": {
545
+ "key_id": founder_key_id,
546
+ "ed25519_pubkey_b64": BASE64.encode(&probe.classical.public_key),
547
+ "ml_dsa_65_pubkey_b64": BASE64.encode(&probe.pqc.public_key),
548
+ }
549
+ });
550
+ if let Some(pin) = claim_pin {
551
+ claim["claim_pin"] = pin.into();
552
+ }
553
+ let body = serde_json::to_vec(&claim).unwrap();
554
+ let sig = fsigner.sign_hybrid(&body).await.expect("sign setup body");
555
+ (
556
+ body,
557
+ BASE64.encode(&sig.classical.signature),
558
+ BASE64.encode(&sig.pqc.signature),
559
+ )
560
+ }
561
+
562
+ /// The correct PIN + valid founder sig + correct node pin → 201, ROOT bound. The
563
+ /// happy path is already covered by `first_run_setup_root_claims_then_rejects_second_claim`
564
+ /// (which supplies `TEST_CLAIM_PIN`); this test additionally asserts the PIN is
565
+ /// CONSUMED after a successful claim — a fresh, validly-signed claim with the SAME
566
+ /// correct PIN is now rejected (the route is 409-closed, AND the PIN is cleared).
567
+ #[tokio::test]
568
+ async fn claim_pin_is_consumed_after_successful_claim() {
569
+ let node_key_id = "ciris-pin-consume-node";
570
+ let founder_key_id = "ciris-pin-consume-founder";
571
+ let engine = node(node_key_id).await;
572
+ register_self(&engine, node_key_id).await;
573
+ let (base, _h) = serve(Arc::clone(&engine), node_key_id).await; // armed with TEST_CLAIM_PIN
574
+ let client = reqwest::Client::new();
575
+
576
+ let (body, ed_b64, mldsa_b64) =
577
+ signed_claim_with_pin(node_key_id, founder_key_id, Some(TEST_CLAIM_PIN)).await;
578
+ let resp = client
579
+ .post(format!("{base}/v1/setup/root"))
580
+ .header("x-ciris-signing-key-id", founder_key_id)
581
+ .header("x-ciris-signature-ed25519", &ed_b64)
582
+ .header("x-ciris-signature-ml-dsa-65", &mldsa_b64)
583
+ .body(body)
584
+ .send()
585
+ .await
586
+ .expect("POST setup/root (correct PIN)");
587
+ assert_eq!(resp.status(), 201, "correct PIN → claim succeeds");
588
+ assert_eq!(
589
+ store::list_by_role(&engine, WaRole::Root, 10)
590
+ .await
591
+ .expect("list roots")
592
+ .len(),
593
+ 1,
594
+ "exactly one ROOT after a correct-PIN claim"
595
+ );
596
+
597
+ // A second VALID claim (fresh founder, same correct PIN) is now rejected: the
598
+ // route is first-run-closed (409) AND the PIN was consumed.
599
+ let (body2, ed2, ml2) =
600
+ signed_claim_with_pin(node_key_id, "ciris-pin-replayer", Some(TEST_CLAIM_PIN)).await;
601
+ let resp2 = client
602
+ .post(format!("{base}/v1/setup/root"))
603
+ .header("x-ciris-signing-key-id", "ciris-pin-replayer")
604
+ .header("x-ciris-signature-ed25519", &ed2)
605
+ .header("x-ciris-signature-ml-dsa-65", &ml2)
606
+ .body(body2)
607
+ .send()
608
+ .await
609
+ .expect("POST setup/root (replay)");
610
+ assert_eq!(
611
+ resp2.status(),
612
+ 409,
613
+ "after a successful claim the route is first-run-closed (PIN also consumed)"
614
+ );
615
+ assert_eq!(
616
+ store::list_by_role(&engine, WaRole::Root, 10)
617
+ .await
618
+ .expect("list roots")
619
+ .len(),
620
+ 1,
621
+ "no second ROOT minted on replay"
622
+ );
623
+ }
624
+
625
+ /// A WRONG PIN (valid founder sig, correct node pin) → 401, NO ROOT minted.
626
+ #[tokio::test]
627
+ async fn claim_with_wrong_pin_is_rejected() {
628
+ let node_key_id = "ciris-wrongpin2-node";
629
+ let founder_key_id = "ciris-wrongpin2-founder";
630
+ let engine = node(node_key_id).await;
631
+ register_self(&engine, node_key_id).await;
632
+ let (base, _h) = serve(Arc::clone(&engine), node_key_id).await; // armed with TEST_CLAIM_PIN
633
+ let client = reqwest::Client::new();
634
+
635
+ // A signed claim carrying the WRONG PIN (same length to exercise the
636
+ // byte-compare path, not just the length flag).
637
+ let (body, ed_b64, mldsa_b64) =
638
+ signed_claim_with_pin(node_key_id, founder_key_id, Some("WRNG-PIN0")).await;
639
+ let resp = client
640
+ .post(format!("{base}/v1/setup/root"))
641
+ .header("x-ciris-signing-key-id", founder_key_id)
642
+ .header("x-ciris-signature-ed25519", &ed_b64)
643
+ .header("x-ciris-signature-ml-dsa-65", &mldsa_b64)
644
+ .body(body)
645
+ .send()
646
+ .await
647
+ .expect("POST setup/root (wrong PIN)");
648
+ assert_eq!(resp.status(), 401, "a wrong PIN is rejected");
649
+ assert!(
650
+ store::list_by_role(&engine, WaRole::Root, 10)
651
+ .await
652
+ .expect("list roots")
653
+ .is_empty(),
654
+ "a wrong-PIN claim mints NO ROOT"
655
+ );
656
+ // The founder key must NOT have been admitted either.
657
+ assert!(
658
+ engine
659
+ .federation_directory()
660
+ .lookup_public_key(founder_key_id)
661
+ .await
662
+ .expect("lookup founder")
663
+ .is_none(),
664
+ "a wrong-PIN claim must NOT register the founder key"
665
+ );
666
+ }
667
+
668
+ /// A MISSING PIN (valid founder sig, correct node pin) → 401, NO ROOT minted.
669
+ #[tokio::test]
670
+ async fn claim_with_missing_pin_is_rejected() {
671
+ let node_key_id = "ciris-nopin-node";
672
+ let founder_key_id = "ciris-nopin-founder";
673
+ let engine = node(node_key_id).await;
674
+ register_self(&engine, node_key_id).await;
675
+ let (base, _h) = serve(Arc::clone(&engine), node_key_id).await; // armed with TEST_CLAIM_PIN
676
+ let client = reqwest::Client::new();
677
+
678
+ // No `claim_pin` field at all.
679
+ let (body, ed_b64, mldsa_b64) = signed_claim_with_pin(node_key_id, founder_key_id, None).await;
680
+ let resp = client
681
+ .post(format!("{base}/v1/setup/root"))
682
+ .header("x-ciris-signing-key-id", founder_key_id)
683
+ .header("x-ciris-signature-ed25519", &ed_b64)
684
+ .header("x-ciris-signature-ml-dsa-65", &mldsa_b64)
685
+ .body(body)
686
+ .send()
687
+ .await
688
+ .expect("POST setup/root (missing PIN)");
689
+ assert_eq!(resp.status(), 401, "a missing PIN is rejected");
690
+ assert!(
691
+ store::list_by_role(&engine, WaRole::Root, 10)
692
+ .await
693
+ .expect("list roots")
694
+ .is_empty(),
695
+ "a missing-PIN claim mints NO ROOT"
696
+ );
697
+ }
698
+
699
+ /// An UN-ARMED node (router built with `claim_pin: None`, e.g. already-claimed at
700
+ /// boot) rejects every claim with 401 even with an otherwise-valid signed body —
701
+ /// the PIN gate fails closed when no PIN is armed.
702
+ #[tokio::test]
703
+ async fn unarmed_node_rejects_claims() {
704
+ let node_key_id = "ciris-unarmed-node";
705
+ let founder_key_id = "ciris-unarmed-founder";
706
+ let engine = node(node_key_id).await;
707
+ register_self(&engine, node_key_id).await;
708
+ // No ROOT yet, but the router is NOT armed with a PIN.
709
+ let (base, _h) = serve_with_pin(Arc::clone(&engine), node_key_id, None).await;
710
+ let client = reqwest::Client::new();
711
+
712
+ let (body, ed_b64, mldsa_b64) =
713
+ signed_claim_with_pin(node_key_id, founder_key_id, Some(TEST_CLAIM_PIN)).await;
714
+ let resp = client
715
+ .post(format!("{base}/v1/setup/root"))
716
+ .header("x-ciris-signing-key-id", founder_key_id)
717
+ .header("x-ciris-signature-ed25519", &ed_b64)
718
+ .header("x-ciris-signature-ml-dsa-65", &mldsa_b64)
719
+ .body(body)
720
+ .send()
721
+ .await
722
+ .expect("POST setup/root (unarmed)");
723
+ assert_eq!(resp.status(), 401, "an un-armed node rejects all claims");
724
+ assert!(
725
+ store::list_by_role(&engine, WaRole::Root, 10)
726
+ .await
727
+ .expect("list roots")
728
+ .is_empty(),
729
+ "an un-armed-node claim mints NO ROOT"
730
+ );
731
+ }
732
+
733
+ /// Unit: `generate_claim_pin` produces an `XXXX-XXXX` Crockford-base32 PIN, and
734
+ /// successive PINs differ (random source).
735
+ #[test]
736
+ fn generate_claim_pin_shape_and_randomness() {
737
+ let p1 = bootstrap::generate_claim_pin();
738
+ assert_eq!(p1.len(), 9, "8 chars + 1 dash");
739
+ assert_eq!(&p1[4..5], "-", "dash splits the two groups");
740
+ let groups: Vec<&str> = p1.split('-').collect();
741
+ assert_eq!(groups.len(), 2);
742
+ assert!(groups.iter().all(|g| g.len() == 4));
743
+ // Crockford alphabet only (no I/L/O/U; uppercase + digits).
744
+ const ALPHABET: &str = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
745
+ assert!(p1
746
+ .chars()
747
+ .filter(|&c| c != '-')
748
+ .all(|c| ALPHABET.contains(c)));
749
+ // Overwhelmingly likely to differ (40 bits of entropy).
750
+ let p2 = bootstrap::generate_claim_pin();
751
+ assert_ne!(p1, p2, "successive PINs differ");
752
+ }
753
+
512
754
  // ─── (d) auto_mint_root_if_needed elevates an admin-eligible identity ────────
513
755
 
514
756
  #[tokio::test]
File without changes
File without changes