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.
- {ciris_server-0.4.1 → ciris_server-0.4.2}/Cargo.lock +1 -1
- {ciris_server-0.4.1 → ciris_server-0.4.2}/Cargo.toml +1 -1
- {ciris_server-0.4.1 → ciris_server-0.4.2}/PKG-INFO +1 -1
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/bootstrap.rs +190 -4
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/compose.rs +33 -3
- {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/root_bootstrap.rs +244 -2
- {ciris_server-0.4.1 → ciris_server-0.4.2}/.cargo/config.toml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/.github/workflows/bench.yml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/.github/workflows/ci.yml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/.github/workflows/conformance.yml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/.github/workflows/publish-pypi.yml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/.github/workflows/release.yml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/.gitignore +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/CHANGELOG.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/LENS_TO_SERVER_MIGRATION.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/MESH_ANIMATION_BRIEF.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/MODERATION_CHILD_SAFETY.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/ONE_WHEEL_REEXPORT.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/PQC_AV_STREAMING_BENCH.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/QA_AGAINST_RUST.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/REGISTRY_FOLD_DERISK.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/SAFETY_LANDSCAPE.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/SERVER_1.0_PLAN.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/FSD/WATCHLIST_DETECTION.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/LICENSE +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/MISSION.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/README.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/.gitignore +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/README.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/build.gradle.kts +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/build.gradle.kts +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/auth/FederationSigner.kt +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/net/FabricClient.kt +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/net/FabricEndpoints.kt +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/platform/PlatformLogger.kt +0 -0
- {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
- {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
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/viewmodel/ErasureViewModel.kt +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonMain/kotlin/ai/ciris/fabric/viewmodel/IdentityViewModel.kt +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/fabric-client/src/commonTest/kotlin/ai/ciris/fabric/IdentitySliceTest.kt +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/gradle/wrapper/gradle-wrapper.jar +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/gradle/wrapper/gradle-wrapper.properties +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/gradle.properties +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/gradlew +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/gradlew.bat +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/app/settings.gradle.kts +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/bench-site/data.json +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/bench-site/index.html +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/benches/alm_chain.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/benches/pqc_av_streaming.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/benches/replication_ingest.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/benches/stream_fanout.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/.github/workflows/bench.yml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/.github/workflows/ci.yml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/.gitignore +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/.pre-commit-config.yaml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/Cargo.toml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/FSD/CIRIS_LENS_CORE.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/FSD/LENS_CORE_V0_5.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/FSD/OPEN_QUESTIONS.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/LICENSE +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/MISSION.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/README.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/benches/aggregate.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/benches/canonicalize.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/benches/project.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/deny.toml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/BENCHMARKS.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/COHABITATION.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/PRE_COMMIT.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/PUBLIC_SCHEMA_CONTRACT.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/PYPI_PUBLISH.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/RELEASE_NOTES.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/STANDARDS_COMPARISON.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/docs/THREAT_MODEL.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/extraction/json_path.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/extraction/metadata.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/extraction/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/distilbert_loader.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/fields.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/ner.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/ort_loader.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/proptests.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/regex.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/patterns_from_cirislens_core/scrubber/walker.rs +0 -0
- {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
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/pyproject.toml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/python/ciris_lens_core/__init__.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/audit/api.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/audit/delegate.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/audit/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/audit/pyo3.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/canonical/ceg_egress.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/canonical/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capacity/attestation.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capacity/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capacity/score.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/batch.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/client.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/consent.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/correlation.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/event.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/partial.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/py_engine.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/capture/seal.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/cohort/declared.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/cohort/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/cohort/resourcing.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/config/egress.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/config/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/config/node.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/config/retention.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/config/upstream.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/detector/axis_metrics.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/detector/coherence_ratchet.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/detector/correlated_action.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/detector/distributive_access.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/detector/manifold.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/detector/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/extract/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/extract/projection.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/ffi/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/ffi/pyo3.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/lib.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/observability/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/pipeline/lifecycle.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/pipeline/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/retention/eviction.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/retention/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/retention/summary.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/role/handler.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/role/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/role/node.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/role/relay.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/role/ret_relay.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scores/aggregate.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scores/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scores/oracle.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/assembly.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/axis_calibration.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/calibration.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/capacity.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/n_eff.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/scoring/result.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/signing/event.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/signing/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/wire/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/src/wire/signer.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/tests/parity/README.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/tests/parity/canonical_fixtures.json +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/tests/parity/generate_canonical_fixtures.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/crates/ciris-lens-core/tests/sovereign_rlib.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/pyproject.toml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/README.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/README.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/REFACTORING_SUMMARY.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/__init__.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/auth_store.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/client.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/examples/__init__.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/examples/agent_interaction.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/examples/unified_telemetry_examples.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/examples/wa_example.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/exceptions.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/model_types.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/models.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/pagination.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/rate_limiter.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/CONFIG_CLIENT.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/WA_CLIENT_GUIDE.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/__init__.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/agent.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/audit.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/auth.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/billing.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/config.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/consent.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/emergency.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/jobs.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/memory.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/setup.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/system.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/telemetry.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/wa.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/resources/websocket.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/setup.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/telemetry_models.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/telemetry_responses.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/transport.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/ciris_sdk/websocket.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/fabric_results.json +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/BILLING_QA_README.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/CLAUDE.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/README.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/SDK_MODULE_INTEGRATION.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/__init__.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/__main__.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/config.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/mcp_test_server.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/CLAUDE.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/MULTI_OCCURRENCE_README.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/__init__.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/_module_metadata.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/accord_metrics_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/accord_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/adapter_autoload_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/adapter_availability_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/adapter_config_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/adapter_manifest_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/agent_mode_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/air_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/api_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/base_test_module.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/billing_integration_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/billing_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/cirisnode_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/cognitive_state_api_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/comprehensive_api_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/comprehensive_single_step_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/consent_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/context_enrichment_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/deferral_taxonomy_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/deferral_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/degraded_mode_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/desktop_viz.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/dream_live_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/dsar_multi_source_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/dsar_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/dsar_ticket_workflow_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/filter_test_helper.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/filter_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/handler_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/he300_benchmark_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/homeassistant_agentic_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/hosted_tools_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/identity_update_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/l4_attestation_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/licensed_agent_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mcp_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/memory_benchmark_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/message_id_debug_test.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/__init__.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/__main__.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/adb_helper.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/device_helper.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/first_time_licensed_agent.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios/__init__.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios/build_helper.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios/idevice_helper.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios/ios_ui_automator.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios/vision_helper.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios/xcrun_helper.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios_physical_test_cases.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ios_test_cases.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/portal_registration.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/test_cases.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/test_runner.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/ui_automator.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/mobile/verify_trust_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/model_eval_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/multi_occurrence_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/parallel_locales_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/partnership_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/play_live_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/reddit_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/safety_battery.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/safety_interpret.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/sdk_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/secrets_encryption_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/setup_e2e_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/setup_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/simple_single_step_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/solitude_live_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/sql_external_data_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/state_transition_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/streaming_verification.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/system_messages_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/utility_adapters_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/vision_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/wallet_tests.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/CLAUDE.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/__init__.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/__main__.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/browser_helper.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/desktop_app_helper.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/desktop_test.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/federation_screen_tags.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/federation_walk_report.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/federation_walk_test.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/licensed_agent_flow.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/server_manager.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/test_cases.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/test_federation_walk_unit.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/modules/web_ui/test_runner.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_api_test.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports/qa_report_20250825_073205.json +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports/qa_report_20250825_153259.json +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports/qa_report_20250825_154628.json +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports/qa_report_20250825_154817.json +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports/qa_report_20250825_155917.json +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports_comprehensive/qa_report_20250825_075504.json +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports_final/qa_report_20250825_092420.json +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_reports_test/qa_report_20250825_092452.json +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_scripts/README.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_scripts/qa_api_test.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_scripts/qa_v1.0_sdk_comprehensive.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_scripts/qa_v1.0_unhealthy_services_check.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_test.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/qa_test_sdk.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/requirements.txt +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/runner.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/scripts/__init__.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/scripts/he300_stream_debug.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/server.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/staged_env.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/status_tracker.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/test_data/dsar_multi_source_privacy_schema.yaml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/test_data/qa_test.db +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/test_data/qa_test_privacy_schema.yaml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/qa_runner/test_data/sql_config.json +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/run_fabric.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/qa/seed_wa_cert.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/rust-toolchain.toml +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/scripts/bench_report.py +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/ABSORPTION.md +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/api_keys.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/attestation.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/consent.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/device_auth.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/erasure.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/gate.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/oauth.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/roles.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/self_login.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/session.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/store.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/auth/verify.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/benchmarks/mod.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/benchmarks/scoreboard.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/config.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/federation_admin.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/federation_nodecode.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/import.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/lib.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/main.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/nodecode.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/peer.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/quorum.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/scorer/n_eff.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/src/scorer.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/alm_chain.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/capacity_scorer.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/chaos_mesh.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/federation_admin.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/nodecode.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/noise_floor.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/peer_replication.rs +0 -0
- {ciris_server-0.4.1 → ciris_server-0.4.2}/tests/replication.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "ciris-server"
|
|
3
|
-
version = "0.4.
|
|
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.
|
|
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 —
|
|
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) =>
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|