From e7273c9191cf6bdbc3ff60d75a48afa32e9f99ba Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 26 Jun 2026 22:34:13 +0000 Subject: [PATCH] refactor: de-vendor foreign services and retire claim-forge + cicd-hyper-a Removes three services that should never have been built/published from OPSM, and stops OPSM publishing any satellite images. GitHub linked their GHCR packages to OPSM because docker/metadata-action stamps org.opencontainers.image.source with the building repo; each real owner now publishes its own correctly-attributed image instead. De-vendor (delete vendored sources + all references): - claim-forge: private code that should not have been public. - cicd-hyper-a: retired (the live project is the separate `hypatia` repo); removed entirely from OPSM, NOT integrated. - cerro-torre: belongs to hyperpolymath/stapeln and is not for release. Deleted services/{claim-forge,cicd-hyper-a,cerro-torre}/ and retired .github/workflows/trust-pipeline-images.yml (cerro-torre was its last matrix entry, so OPSM now publishes no satellite images at all). Elixir core retirement (claim_forge + cicd_hyper_a): - Deleted the ClaimForge and CicdHyperA client modules. - config.ex: dropped both service fields, default ports (7001, 7004), the parser clauses and example config. - types.ex: removed both OpsmConfig fields, the cicd publish/federation types (SyncState, FederationStatus, CicdPublishRequest/Response) and the claim-forge types (claim_type, ClaimForgeRequest/Response); dropped :claim_forge from the attestation_type union (kept :sigstore | :in_toto and AttestationRef, still used by ResolvedPackage and the SLSA check). - wiring.ex: removed the claim-forge attestation stage and the cicd-hyper-a publish stage; run_publish now does ingest -> license (palimpsest) -> sustainability (oikos) -> validate -> checky-monkey. - events.ex: genericised the federation event dispatcher off CicdHyperA (it only ever simulated a response). - trust/pipeline.ex: dropped the claim-forge SPDX attestation step from verify_for_publish; errors.ex: dropped the two *_unavailable severities; cli.ex/container.ex: doc/help text. - Updated config_test, pipeline_test, e2e_test, trust_pipeline_test and the live-service e2e test accordingly. Proven locally: `mix compile --warnings-as-errors` is clean for all changed modules and `mix test` is green (the sole failure is an unrelated, pre-existing network-dependent registry test that hits api.github.com). Also scrubbed the references from the ReScript/OCaml CLI mirrors (cli/, lib/ocaml/), opsm.toml, the groove manifest, stapeln.toml, the selur-compose / k8s deployment files, the trust-pipeline-e2e workflow, the A2ML metadata (STATE/ECOSYSTEM/META), the mobile controller, and the docs. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_012kWck3NjBo5orHKAXMHm5W --- .github/workflows/trust-pipeline-e2e.yml | 18 +- .github/workflows/trust-pipeline-images.yml | 92 - .machine_readable/6a2/ECOSYSTEM.a2ml | 12 - .machine_readable/6a2/META.a2ml | 4 +- .machine_readable/6a2/STATE.a2ml | 17 +- .well-known/groove/manifest.json | 2 - CHARTER.adoc | 2 +- EXPLAINME.adoc | 11 +- READINESS.md | 4 +- README.adoc | 70 +- ROADMAP.adoc | 13 +- TEST-NEEDS.md | 6 +- TESTING.md | 5 +- TOPOLOGY.md | 12 +- cli/Config.res | 15 - cli/Config.res.js | 18 - cli/Types.res | 76 +- cli/Wiring.res | 81 +- cli/Wiring.res.js | 67 +- cli/clients/CicdHyperA.res | 249 -- cli/clients/CicdHyperA.res.js | 253 -- cli/clients/ClaimForge.res | 248 -- cli/clients/ClaimForge.res.js | 284 -- deploy/k8s/microservices.yaml | 146 +- deploy/k8s/opsm-deployment.yaml | 4 - deploy/selur-compose.yaml | 58 - docker-compose.dev.yml | 2 - docs/CLI-FEATURE-COMPARISON.md | 10 +- docs/CONTAINER-INTEGRATION.md | 21 +- docs/MOBILE-API.md | 10 +- docs/PAPER-SKETCH-zenodo-arxiv.md | 8 +- docs/architecture.adoc | 4 +- docs/service-contracts.adoc | 154 +- lib/ocaml/CicdHyperA.cmj | Bin 378 -> 0 bytes lib/ocaml/CicdHyperA.res | 249 -- lib/ocaml/ClaimForge.cmj | Bin 335 -> 0 bytes lib/ocaml/ClaimForge.res | 248 -- lib/ocaml/Config.res | 15 - lib/ocaml/Types.res | 76 +- lib/ocaml/Wiring.res | 81 +- opsm-registry-hub/trust-pipeline/README.adoc | 3 +- opsm.toml | 8 - opsm_ex/CHANGELOG.md | 8 +- opsm_ex/Containerfile | 2 +- opsm_ex/TEST-EXECUTION-REPORT.md | 33 +- opsm_ex/lib/opsm/cli.ex | 2 +- opsm_ex/lib/opsm/clients/cicd_hyper_a.ex | 141 - opsm_ex/lib/opsm/clients/claim_forge.ex | 113 - opsm_ex/lib/opsm/config.ex | 10 +- opsm_ex/lib/opsm/container.ex | 1 - opsm_ex/lib/opsm/errors.ex | 2 - opsm_ex/lib/opsm/events.ex | 21 +- opsm_ex/lib/opsm/trust/pipeline.ex | 26 +- opsm_ex/lib/opsm/types.ex | 79 +- opsm_ex/lib/opsm/wiring.ex | 93 +- opsm_ex/test/integration/e2e_test.exs | 8 +- opsm_ex/test/integration/pipeline_test.exs | 2 - .../trust_pipeline_live_e2e_test.exs | 267 +- .../test/integration/trust_pipeline_test.exs | 216 +- opsm_ex/test/opsm/config_test.exs | 14 +- opsm_mobile/api/package_controller.ex | 2 - runtime/tools/minter/generate-rsr-repo.sh | 2 +- selur-compose.yml | 102 +- services/cerro-torre/.tool-versions | 1 - services/cerro-torre/Cargo.lock | 1852 ------------- services/cerro-torre/Cargo.toml | 39 - services/cerro-torre/Containerfile | 49 - services/cerro-torre/README.md | 361 --- services/cerro-torre/src/main.rs | 475 ---- services/cicd-hyper-a/.tool-versions | 1 - services/cicd-hyper-a/Cargo.lock | 1212 --------- services/cicd-hyper-a/Cargo.toml | 30 - services/cicd-hyper-a/Containerfile | 41 - services/cicd-hyper-a/src/main.rs | 485 ---- services/claim-forge/.tool-versions | 1 - services/claim-forge/Cargo.lock | 2335 ----------------- services/claim-forge/Cargo.toml | 40 - services/claim-forge/Containerfile | 50 - services/claim-forge/src/main.rs | 229 -- stapeln.toml | 32 +- 80 files changed, 195 insertions(+), 10838 deletions(-) delete mode 100644 .github/workflows/trust-pipeline-images.yml delete mode 100644 cli/clients/CicdHyperA.res delete mode 100644 cli/clients/CicdHyperA.res.js delete mode 100644 cli/clients/ClaimForge.res delete mode 100644 cli/clients/ClaimForge.res.js delete mode 100644 lib/ocaml/CicdHyperA.cmj delete mode 100644 lib/ocaml/CicdHyperA.res delete mode 100644 lib/ocaml/ClaimForge.cmj delete mode 100644 lib/ocaml/ClaimForge.res delete mode 100644 opsm_ex/lib/opsm/clients/cicd_hyper_a.ex delete mode 100644 opsm_ex/lib/opsm/clients/claim_forge.ex delete mode 100644 services/cerro-torre/.tool-versions delete mode 100644 services/cerro-torre/Cargo.lock delete mode 100644 services/cerro-torre/Cargo.toml delete mode 100644 services/cerro-torre/Containerfile delete mode 100644 services/cerro-torre/README.md delete mode 100644 services/cerro-torre/src/main.rs delete mode 100644 services/cicd-hyper-a/.tool-versions delete mode 100644 services/cicd-hyper-a/Cargo.lock delete mode 100644 services/cicd-hyper-a/Cargo.toml delete mode 100644 services/cicd-hyper-a/Containerfile delete mode 100644 services/cicd-hyper-a/src/main.rs delete mode 100644 services/claim-forge/.tool-versions delete mode 100644 services/claim-forge/Cargo.lock delete mode 100644 services/claim-forge/Cargo.toml delete mode 100644 services/claim-forge/Containerfile delete mode 100644 services/claim-forge/src/main.rs diff --git a/.github/workflows/trust-pipeline-e2e.yml b/.github/workflows/trust-pipeline-e2e.yml index 8a099e47..9c36752a 100644 --- a/.github/workflows/trust-pipeline-e2e.yml +++ b/.github/workflows/trust-pipeline-e2e.yml @@ -1,9 +1,9 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# Enterprise trust pipeline — live-service E2E tests. +# Trust pipeline — live-service E2E tests. # -# Pulls all 5 trust-tier service images from GHCR (public, no auth required), +# Pulls the trust-tier service images from GHCR (public, no auth required), # starts them via selur-compose.yml, waits for health checks, then runs the # :live_service E2E suite against the real HTTP endpoints. # @@ -65,28 +65,22 @@ jobs: # ----------------------------------------------------------------------- run: | - docker pull ghcr.io/hyperpolymath/claim-forge:latest docker pull ghcr.io/hyperpolymath/checky-monkey:latest docker pull ghcr.io/hyperpolymath/palimpsest-license:latest - docker pull ghcr.io/hyperpolymath/cicd-hyper-a:latest docker pull ghcr.io/hyperpolymath/oikos:latest - - name: Start trust pipeline (claim-forge, checky-monkey, palimpsest, cicd-hyper-a, oikos) + - name: Start trust pipeline (checky-monkey, palimpsest, oikos) run: | - # Start only the 5 trust-tier services (not the optional container-security profile) + # Start only the trust-tier services (not the optional container-security profile) docker compose -f selur-compose.yml up -d \ - claim-forge \ checky-monkey \ palimpsest \ - cicd-hyper-a \ oikos - name: Wait for all services to be healthy # Polls each /health endpoint; aborts if any service fails to start within 3 minutes run: | services=( - "http://localhost:8080/health:claim-forge" "http://localhost:8081/health:checky-monkey" "http://localhost:8082/health:palimpsest" - "http://localhost:8083/health:cicd-hyper-a" "http://localhost:8084/health:oikos" ) deadline=$(( $(date +%s) + 180 )) @@ -111,10 +105,8 @@ jobs: - name: Run trust pipeline E2E tests working-directory: opsm_ex env: - CLAIM_FORGE_URL: "http://localhost:8080" CHECKY_MONKEY_URL: "http://localhost:8081" PALIMPSEST_URL: "http://localhost:8082" - CICD_HYPER_A_URL: "http://localhost:8083" OIKOS_URL: "http://localhost:8084" MIX_ENV: test run: | @@ -129,7 +121,7 @@ jobs: - name: Dump service logs on failure if: failure() run: | - for svc in claim-forge checky-monkey palimpsest cicd-hyper-a oikos; do + for svc in checky-monkey palimpsest oikos; do echo "=== $svc logs ===" docker compose -f selur-compose.yml logs "$svc" --tail=50 || true done diff --git a/.github/workflows/trust-pipeline-images.yml b/.github/workflows/trust-pipeline-images.yml deleted file mode 100644 index c0106cdc..00000000 --- a/.github/workflows/trust-pipeline-images.yml +++ /dev/null @@ -1,92 +0,0 @@ -# // Copyright (c) Jonathan D.A. Jewell -# SPDX-License-Identifier: MPL-2.0 -# Build and push OPSM-native service images to GHCR. -# -# Only services that have NO upstream repository of their own are built here. -# Projects with their own repo publish their own images, so that each GHCR -# package -> repository link points at the real source rather than at OPSM. -# (That link is driven by the org.opencontainers.image.source OCI label, which -# docker/metadata-action injects automatically from the repo running the build — -# which is why building a foreign service here silently mis-attributed it to -# odds-and-sods-package-manager.) -# -# Built / published elsewhere (do NOT add back here): -# selur, vordr -> hyperpolymath/stapeln -# checky-monkey -> hyperpolymath/checky-monkey -# palimpsest-license -> hyperpolymath/palimpsest-license -# svalinn -> hyperpolymath/svalinn -# oikos -> hyperpolymath/oikosbot -# cicd-hyper-a -> retired; no longer exists, GHCR package to be deleted -# claim-forge -> owner's private system; must not be published publicly here -# -# OPSM-native service(s) built here: -# cerro-torre (pending confirmation it has no upstream/private home of its own) - -name: Trust Pipeline Images -on: - push: - branches: [main] - paths: - - "services/**" - - ".github/workflows/trust-pipeline-images.yml" - workflow_dispatch: -permissions: - contents: read - packages: write -jobs: - build-push: - name: Build ${{ matrix.service }} - runs-on: ubuntu-latest - timeout-minutes: 15 - permissions: - contents: read - packages: write - id-token: write # mint the OIDC token the attestation is signed with - attestations: write # write the build-provenance attestation (the "claim") - strategy: - fail-fast: false - matrix: - include: - # OPSM-native services only. Anything with its own repo or that belongs - # to a private system is published by its owner, so each GHCR package - # links to the correct source: selur/vordr -> stapeln, checky-monkey, - # palimpsest-license, svalinn, oikos -> oikosbot. cicd-hyper-a is - # retired; claim-forge belongs to a private system. cerro-torre is the - # only one still built here, pending confirmation it has no other home. - - service: cerro-torre - steps: - - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - - name: Log in to GHCR - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata - id: meta - uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 - with: - images: ghcr.io/hyperpolymath/${{ matrix.service }} - tags: | - type=raw,value=latest,enable={{is_default_branch}} - type=sha - - name: Build and push ${{ matrix.service }} - id: push - uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v6 - with: - context: services/${{ matrix.service }} - file: services/${{ matrix.service }}/Containerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha,scope=${{ matrix.service }} - cache-to: type=gha,mode=max,scope=${{ matrix.service }} - - name: Attest container provenance - uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2 - with: - subject-name: ghcr.io/hyperpolymath/${{ matrix.service }} - subject-digest: ${{ steps.push.outputs.digest }} - push-to-registry: true diff --git a/.machine_readable/6a2/ECOSYSTEM.a2ml b/.machine_readable/6a2/ECOSYSTEM.a2ml index 8bfb6225..0ce3578d 100644 --- a/.machine_readable/6a2/ECOSYSTEM.a2ml +++ b/.machine_readable/6a2/ECOSYSTEM.a2ml @@ -21,12 +21,6 @@ name = "nextgen-databases" relationship = "consumer" description = "Consumes OPSM workspace operations and custom forth adapters for database toolchain packages." -[[related-projects.co-located-service]] -name = "claim-forge" -relationship = "sibling-service" -location = "services/claim-forge" -description = "Trust-pipeline service; verifies claim provenance. Co-located in services/." - [[related-projects.co-located-service]] name = "checky-monkey" relationship = "sibling-service" @@ -39,12 +33,6 @@ relationship = "sibling-service" location = "services/palimpsest" description = "Trust-pipeline service; licence compliance verification. Co-located in services/." -[[related-projects.co-located-service]] -name = "cicd-hyper-a" -relationship = "sibling-service" -location = "services/cicd-hyper-a" -description = "Trust-pipeline service; CI/CD orchestration helper. Co-located in services/." - [[related-projects.co-located-service]] name = "oikos" relationship = "sibling-service" diff --git a/.machine_readable/6a2/META.a2ml b/.machine_readable/6a2/META.a2ml index a1729caf..4553684e 100644 --- a/.machine_readable/6a2/META.a2ml +++ b/.machine_readable/6a2/META.a2ml @@ -55,8 +55,8 @@ status = "accepted" title = "rustls-tls over native-tls in all Rust services" decision = """ Switch all reqwest TLS backend selections from native-tls (openssl-sys) to -rustls-tls across all Rust services in the project (claim-forge, opsm_mobile, -selur, svalinn, vordr, cerro-torre, and any future Rust crates). +rustls-tls across all Rust services in the project (opsm_mobile, +selur, svalinn, vordr, and any future Rust crates). """ rationale = """ openssl-sys requires a C toolchain and platform OpenSSL headers, creating diff --git a/.machine_readable/6a2/STATE.a2ml b/.machine_readable/6a2/STATE.a2ml index 6daed6a4..d296e752 100644 --- a/.machine_readable/6a2/STATE.a2ml +++ b/.machine_readable/6a2/STATE.a2ml @@ -95,17 +95,14 @@ runtime-layout = "~/.opsm/runtimes/// + .active pin file" - nif-build.yml: CI workflow that compiles opsm_pq_nif + runs :requires_nif tests ✅ - test/opsm/wiring/audit_test.exs: 12 tests for Wiring.run_audit/2 + workspace TOML parsing ✅ - test/opsm/registries/language_adapters_test.exs: +18 tests for Registry dispatch dispatch ✅ -- All 9 service images live in GHCR (CI run 24929720837 = 100% success): - ghcr.io/hyperpolymath/claim-forge:latest ✅ +- All 6 service images live in GHCR (CI run 24929720837 = 100% success): ghcr.io/hyperpolymath/checky-monkey:latest ✅ ghcr.io/hyperpolymath/palimpsest-license:latest ✅ - ghcr.io/hyperpolymath/cicd-hyper-a:latest ✅ ghcr.io/hyperpolymath/oikos:latest ✅ ghcr.io/hyperpolymath/selur:latest ✅ ghcr.io/hyperpolymath/svalinn:latest ✅ ghcr.io/hyperpolymath/vordr:latest ✅ - ghcr.io/hyperpolymath/cerro-torre:latest ✅ -- rustls-tls switch: selur, svalinn, vordr, cerro-torre — Dependabot vulns 33→13 ✅ +- rustls-tls switch: selur, svalinn, vordr — Dependabot vulns 33→13 ✅ - Containerfile fixes: removed unavailable APK packages (afl++/libfuzzer, falco/bpftrace, opa) ✅ [completed-2026-04-25d] @@ -118,8 +115,8 @@ runtime-layout = "~/.opsm/runtimes/// + .active pin file" - Test suite: 814 tests (e2e+integration included), 2 pre-existing failures, 0 new failures ✅ [completed-2026-04-25e] -- rustls-tls switch: claim-forge dev-dep reqwest + opsm_mobile reqwest 0.11→0.12+rustls-tls ✅ -- Regenerated Cargo.lock for claim-forge + opsm_mobile: openssl removed from both ✅ +- rustls-tls switch: opsm_mobile reqwest 0.11→0.12+rustls-tls ✅ +- Regenerated Cargo.lock for opsm_mobile: openssl removed ✅ - Dependabot vulns: 13 → 1 (10 openssl HIGH/LOW gone; 2 rand LOW gone/stale; glib MODERATE remains) ✅ - glib 0.18.5 BLOCKED: tauri 2.10.3 (latest) still pulls glib 0.18.5; unfixable at consumer level; fix blocked on tauri upstream GTK-rs 0.20 migration @@ -160,15 +157,15 @@ evidence-file = "READINESS.md" [completed-2026-04-25g] - Trust pipeline live-service E2E test: test/integration/trust_pipeline_live_e2e_test.exs ✅ - - 23 tests across 7 describes: health (5), attestation (4), checky-monkey (4), - palimpsest (5), cicd-hyper-a (4), oikos (2), full pipeline (3) + - 19 tests across 5 describes: health (5), checky-monkey (4), + palimpsest (5), oikos (2), full pipeline (3) - Direct HTTP via Req (no Opsm.Clients.* wrapper) — tests real HTTP contract - @moduletag :live_service excluded from default run; included in trust-pipeline-e2e.yml - :live_service added to test_helper.exs exclusion list - .github/workflows/trust-pipeline-e2e.yml ✅ - Triggers: workflow_dispatch + push to main on service/test/compose changes - SHA-pinned actions: checkout (v6.0.2), erlef/setup-beam, actions/cache (v4) - - docker compose up -d claim-forge checky-monkey palimpsest cicd-hyper-a oikos + - docker compose up -d checky-monkey palimpsest oikos - Health poll loop (180s deadline, 3s interval) before running tests - Full diagnostics (service logs) on failure; docker compose down always diff --git a/.well-known/groove/manifest.json b/.well-known/groove/manifest.json index 9843884b..0e0543a9 100644 --- a/.well-known/groove/manifest.json +++ b/.well-known/groove/manifest.json @@ -34,10 +34,8 @@ "registry_gateway": 4050, "mobile_api": 4051, "verisimdb": 6077, - "claim_forge": 7001, "checky_monkey": 7002, "palimpsest_license": 7003, - "cicd_hyper_a": 7004, "oikos": 7005 }, "ipc": { diff --git a/CHARTER.adoc b/CHARTER.adoc index 4bd3455c..6a99205d 100644 --- a/CHARTER.adoc +++ b/CHARTER.adoc @@ -12,7 +12,7 @@ OPSM exists to deliver a federated, multi-language package manager with formal v The stewardship council safeguards: - Verification integrity (proven/Idris2) -- Trust pipeline correctness (checky-monkey, claim-forge, palimpsest-license) +- Trust pipeline correctness (checky-monkey, palimpsest-license) - Federation neutrality (git-private-farm + Radicle + IPFS) - Long-term sustainability (oikos-driven dependency health) diff --git a/EXPLAINME.adoc b/EXPLAINME.adoc index 53f44ad2..304711fc 100644 --- a/EXPLAINME.adoc +++ b/EXPLAINME.adoc @@ -14,11 +14,11 @@ The README makes claims. This file backs them up. *Evidence:* The registry adapters live in `opsm_ex/lib/opsm/registries/` with 8 core implementations (npm, Hex, Crates, PyPI, Nimble, Idris2, git, agentic) and 93 additional adapters reachable via plugin architecture. Each adapter implements the `RegistryAdapter` callback module with `search/1`, `fetch_package/3`, and `fetch_dependencies/3`. The PubGrub resolver at `opsm_ex/lib/opsm/resolver.ex` handles language-specific version constraint semantics: Semver (npm, Hex, Crates), PEP 440 (PyPI), Go MVS (Go modules), etc. ETS caching at `opsm_ex/lib/opsm/registry_cache.ex` provides TTL-based performance. The test suite at `opsm_ex/test/opsm/resolver_test.exs` validates resolution across mixed-language dependency trees. **Caveat:** Not all 101 adapters are fully tested in CI; community adapters may vary in maturity. -=== Claim 2: Trust Pipeline with 5 Microservices and Post-Quantum Cryptography (Rust NIF) +=== Claim 2: Trust Pipeline with 3 Microservices and Post-Quantum Cryptography (Rust NIF) -*From README:* "Trust Pipeline: 5-microservice verification (claim-forge, checky-monkey, palimpsest-license, oikos, cicd-hyper-a)... Post-quantum cryptography (Hybrid Ed25519+Dilithium5 signatures, Kyber-1024 KEM, SPHINCS+) (Rust NIF)." +*From README:* "Trust Pipeline: multi-microservice verification (checky-monkey, palimpsest-license, oikos)... Post-quantum cryptography (Hybrid Ed25519+Dilithium5 signatures, Kyber-1024 KEM, SPHINCS+) (Rust NIF)." -*Evidence:* Every `opsm publish` traverses a 5-stage pipeline: (1) claim-forge generates SLSA attestations at `opsm_ex/lib/opsm/clients/claim_forge.ex`; (2) checky-monkey verifies package integrity with async polling; (3) palimpsest-license checks license compatibility; (4) oikos scores sustainability (8 dimensions); (5) cicd-hyper-a publishes and federates. The post-quantum NIF at `opsm_ex/native/opsm_pq_nif/src/lib.rs` implements Dilithium5 signatures, Kyber-1024 key encapsulation, and SPHINCS+ hashing. The NIF includes a real availability probe and persistent_term caching for performance. Tests at `opsm_ex/test/opsm/verified_test.exs` (40 property-based tests) verify that SSRF is blocked and JSON size/depth limits are enforced. **Caveat:** Post-quantum cryptography is still actively researched; Dilithium5 and Kyber-1024 are NIST finalists but not yet standardized. +*Evidence:* Every `opsm publish` traverses a 3-stage trust pipeline: (1) checky-monkey verifies package integrity with async polling at `opsm_ex/lib/opsm/clients/checky_monkey.ex`; (2) palimpsest-license checks license compatibility; (3) oikos scores sustainability (8 dimensions). The post-quantum NIF at `opsm_ex/native/opsm_pq_nif/src/lib.rs` implements Dilithium5 signatures, Kyber-1024 key encapsulation, and SPHINCS+ hashing. The NIF includes a real availability probe and persistent_term caching for performance. Tests at `opsm_ex/test/opsm/verified_test.exs` (40 property-based tests) verify that SSRF is blocked and JSON size/depth limits are enforced. **Caveat:** Post-quantum cryptography is still actively researched; Dilithium5 and Kyber-1024 are NIST finalists but not yet standardized. === Claim 3: 547 Core Tests, 40 Property Tests, 49 Integration Tests with Formal Verification @@ -49,7 +49,7 @@ OPSM is continuously checked by the gitbot-fleet: - https://github.com/hyperpolymath/seambot[seambot] — Integration testing - https://github.com/hyperpolymath/finishbot[finishbot] — Release readiness -Trust services (claim-forge, checky-monkey, palimpsest-license, oikos, cicd-hyper-a) are shared across all hyperpolymath package publishing workflows. +Trust services (checky-monkey, palimpsest-license, oikos) are shared across all hyperpolymath package publishing workflows. == File Map @@ -63,7 +63,7 @@ Trust services (claim-forge, checky-monkey, palimpsest-license, oikos, cicd-hype | `opsm_ex/lib/opsm/lockfile.ex` | Lockfile management (read/write) | `opsm_ex/lib/opsm/verified.ex` | Security guarantees: URL validation, JSON DoS prevention | `opsm_ex/lib/opsm/registries/` | 8 core registry adapters (npm, Hex, Crates, PyPI, Nimble, Idris2, git, agentic) -| `opsm_ex/lib/opsm/clients/` | Trust service clients (claim-forge, checky-monkey, palimpsest, oikos, cicd-hyper-a) +| `opsm_ex/lib/opsm/clients/` | Trust service clients (checky-monkey, palimpsest, oikos) | `opsm_ex/lib/opsm/crypto/post_quantum.ex` | PQ NIF wrapper with probe and caching | `opsm_ex/native/opsm_pq_nif/src/lib.rs` | Rust NIF: Dilithium5, Kyber-1024, SPHINCS+ | `opsm_ex/test/opsm/` | 547 core tests (resolver, constraints, lockfile, verified) @@ -71,7 +71,6 @@ Trust services (claim-forge, checky-monkey, palimpsest-license, oikos, cicd-hype | `services/svalinn/` | Vulnerability scanning (Trivy + Grype) | `services/selur/` | Image signing (Cosign + Ed25519) | `services/vordr/` | Policy verification (OPA) -| `services/cerro-torre/` | Security monitoring (Falco + eBPF) | `scripts/har-agents/` | HAR discovery agents (GitHub search, web scraper, mirror finder) | `docs/v2.0-PLAN.md` | Roadmap and feature specifications |=== diff --git a/READINESS.md b/READINESS.md index d302ce50..ddda1fa3 100644 --- a/READINESS.md +++ b/READINESS.md @@ -37,7 +37,7 @@ Copyright (c) Jonathan D.A. Jewell - `[runtime]` section: elixir 1.16.0, erlang 26.2.0, just, deno 2.6.10, zig 0.14.0 — all managed by OPSM runtime extension (replaces `.tool-versions`) - `[package]` section: OPSM itself as a first-class HFR package (`forth = "elixir"`, `self_hosted = true`) - `[opsm]` trust config: `trust_level = "hyperpolymath"`, `registry = "hf"` -- Trust pipeline service endpoints: claim-forge/checky-monkey/palimpsest-license/cicd-hyper-a/oikos all configured in same file +- Trust pipeline service endpoints: checky-monkey/palimpsest-license/oikos all configured in same file **Test coverage:** `opsm runtime install --from-manifest` (live_download tag), self-managed HFR metadata via `hfr_test.exs` @@ -100,7 +100,7 @@ Copyright (c) Jonathan D.A. Jewell - `role = "dogfood-wave-1"` — first wave of external dogfooding - `[telemetry] enabled = false` — telemetry opt-out tested -**Distinguishing factor:** exercises OPSM strict policy mode + pathroot routing, which most other targets do not. The strict policy surfaces any permissiveness bugs in the trust pipeline integration; `allow_untrusted = false` forces all packages through the full claim-forge/checky-monkey chain. +**Distinguishing factor:** exercises OPSM strict policy mode + pathroot routing, which most other targets do not. The strict policy surfaces any permissiveness bugs in the trust pipeline integration; `allow_untrusted = false` forces all packages through the full palimpsest-license/checky-monkey chain. --- diff --git a/README.adoc b/README.adoc index 9c11cef0..eafec97f 100644 --- a/README.adoc +++ b/README.adoc @@ -27,8 +27,8 @@ OPSM is the **first universal package manager** that combines: 2. **Post-Quantum Cryptography** - Hybrid Ed25519+Dilithium5 signatures, Kyber-1024 KEM, SPHINCS+ (Rust NIF) 3. **SLSA Level 3 Compliance** - Provenance generation, signature verification, policy enforcement 4. **PubGrub Dependency Resolver** - Semver, PEP 440, Go MVS support with ETS caching -5. **Trust Pipeline** - 5-microservice verification (claim-forge, checky-monkey, palimpsest-license, oikos, cicd-hyper-a) -6. **Container Security** - 4 Rust services (svalinn, selur, vordr, cerro-torre) +5. **Trust Pipeline** - multi-microservice verification (checky-monkey, palimpsest-license, oikos) +6. **Container Security** - 3 Rust services (svalinn, selur, vordr) 7. **Sustainability Scoring** - Oikos-based real package-level analysis integrated into resolver 8. **Formal Verification** - Proven library (SSRF/DoS prevention via property-based testing) 9. **Federation-First** - Event propagation, distributed architecture, cross-registry search @@ -73,17 +73,13 @@ OPSM v1.1.0 introduces a comprehensive container security pipeline with 4 dedica | **vordr** | Runtime policy verification (OPA + built-in) | 8087 - -| **cerro-torre** -| Security monitoring (Falco + eBPF) -| 8088 |=== **Testing Results:** - ✅ Secure container (alpine:3.19): PASSED all checks, 2 vulnerabilities (0 critical) - ✅ Insecure container (nginx): BLOCKED with 9 violations (1 CRITICAL, 4 HIGH, 2 MEDIUM, 2 LOW) -**See:** link:services/svalinn/README.md[Svalinn], link:services/selur/README.md[Selur], link:services/vordr/README.md[Vordr], link:services/cerro-torre/README.md[Cerro-Torre] +**See:** link:services/svalinn/README.md[Svalinn], link:services/selur/README.md[Selur], link:services/vordr/README.md[Vordr] == Current Status (v2.0.0) @@ -98,10 +94,10 @@ OPSM v1.1.0 introduces a comprehensive container security pipeline with 4 dedica - ✅ **PQ NIF compiled** with real availability probe and `persistent_term` caching - ✅ **SLSA Level 3**: provenance generation, signature verification, policy enforcement - ✅ **Cryptographic primitives**: Argon2id, ChaCha20-Poly1305, BLAKE2b, SHA3-512 -- ✅ **Container security pipeline**: 4 Rust microservices (svalinn, selur, vordr, cerro-torre) +- ✅ **Container security pipeline**: 3 Rust microservices (svalinn, selur, vordr) **Ecosystem:** -- ✅ **Trust pipeline**: 5 microservices (claim-forge, checky-monkey, palimpsest-license, oikos, cicd-hyper-a) +- ✅ **Trust pipeline**: 3 microservices (checky-monkey, palimpsest-license, oikos) - ✅ **Oikos sustainability scoring** integrated into resolver with `--sustainability` flag - ✅ **Federation**: 9 language forths + 9 system connection ports - ✅ **Agentic cross-registry search**: parallel search across all registries @@ -235,11 +231,9 @@ opsm install express --registry npm opsm publish ./my-package # Trust pipeline stages: -# 1. claim-forge: Generate attestation -# 2. checky-monkey: Verify package integrity -# 3. palimpsest-license: Check license compatibility -# 4. oikos: Score sustainability (8 dimensions) -# 5. cicd-hyper-a: Publish to registry + federation +# 1. palimpsest-license: Check license compatibility +# 2. oikos: Score sustainability (8 dimensions) +# 3. checky-monkey: Verify package integrity # View published package opsm show my-package --registry @@ -308,22 +302,14 @@ end 2. **Git-based** - Idris2 (curated list), generic Git (any repository) 3. **Agentic** - HAR queue system with 3 discovery agents -=== Trust Pipeline (5 Microservices) +=== Trust Pipeline (3 Microservices) -Every `opsm publish` goes through 5 verification stages: +Every `opsm publish` goes through these verification stages: [cols="1,2,2"] |=== | Service | Purpose | Verification -| **claim-forge** -| Attestation generation -| Creates SLSA-style attestations with package metadata - -| **checky-monkey** -| Package verification -| Async integrity checks (60s timeout, 5s polling) - | **palimpsest-license** | License analysis | Detects conflicts, validates compatibility @@ -332,9 +318,9 @@ Every `opsm publish` goes through 5 verification stages: | Sustainability scoring | 8-dimensional scoring (security, maintenance, docs, tests, etc.) -| **cicd-hyper-a** -| Publication + federation -| Publishes to registry, propagates events to mirrors +| **checky-monkey** +| Package verification +| Async integrity checks (60s timeout, 5s polling) |=== **Error Severity:** @@ -421,20 +407,12 @@ verbosity = "info" [registries] default = "npm" -[claim_forge] -base_url = "http://127.0.0.1:7000" -timeout_ms = 30000 - [checky_monkey] -base_url = "http://127.0.0.1:7001" +base_url = "http://127.0.0.1:7002" timeout_ms = 60000 [palimpsest] -base_url = "http://127.0.0.1:7002" -timeout_ms = 30000 - -[cicd_hyper_a] -base_url = "http://127.0.0.1:7004" +base_url = "http://127.0.0.1:7003" timeout_ms = 30000 [oikos] @@ -477,11 +455,9 @@ odds-and-sods-package-manager/ │ │ ├── crypto/ │ │ │ └── post_quantum.ex # PQ NIF wrapper (probe + cache) │ │ ├── clients/ # Trust service clients -│ │ │ ├── claim_forge.ex │ │ │ ├── checky_monkey.ex │ │ │ ├── palimpsest.ex -│ │ │ ├── oikos.ex -│ │ │ └── cicd_hyper_a.ex +│ │ │ └── oikos.ex │ │ └── package/ │ │ ├── installer.ex # Install pipeline (sustainability wiring) │ │ └── publisher.ex @@ -511,13 +487,8 @@ odds-and-sods-package-manager/ │ │ ├── Cargo.toml │ │ ├── Containerfile │ │ └── README.md -│ ├── vordr/ # Policy verification (OPA + built-in) -│ │ ├── src/main.rs # Policy engine (550 lines) -│ │ ├── Cargo.toml -│ │ ├── Containerfile -│ │ └── README.md -│ └── cerro-torre/ # Security monitoring (Falco + eBPF) -│ ├── src/main.rs # Event monitoring (500 lines) +│ └── vordr/ # Policy verification (OPA + built-in) +│ ├── src/main.rs # Policy engine (550 lines) │ ├── Cargo.toml │ ├── Containerfile │ └── README.md @@ -608,11 +579,9 @@ OPSM requires 5 trust services to be running for publish operations: selur-compose -f deploy/selur-compose.yml up -d # Or deploy systemd services (see RELEASE-v1.0.0.md) -sudo systemctl start claim-forge sudo systemctl start checky-monkey sudo systemctl start palimpsest-license sudo systemctl start oikos -sudo systemctl start cicd-hyper-a ---- === Deploying HAR Agents @@ -765,17 +734,14 @@ See link:CONTRIBUTING.md[CONTRIBUTING.md] for contribution guidelines. OPSM is part of the hyperpolymath ecosystem: **Trust Pipeline Microservices:** -- https://github.com/hyperpolymath/claim-forge[claim-forge] - Attestation generation - https://github.com/hyperpolymath/checky-monkey[checky-monkey] - Package verification - https://github.com/hyperpolymath/palimpsest-license[palimpsest-license] - License analysis - https://github.com/hyperpolymath/oikos[oikos] - Sustainability scoring (8 dimensions) -- https://github.com/hyperpolymath/cicd-hyper-a[cicd-hyper-a] - Publication + federation **Container Security Services (v1.1.0):** - link:services/svalinn/[svalinn] - Vulnerability scanning (Trivy + Grype, port 8085) - link:services/selur/[selur] - Image signing (Cosign + Ed25519, port 8086) - link:services/vordr/[vordr] - Policy verification (OPA + built-in engine, port 8087) -- link:services/cerro-torre/[cerro-torre] - Security monitoring (Falco + eBPF, port 8088) **Mobile App Dependencies:** - https://github.com/hyperpolymath/cadre-router[cadre-router] - Type-safe URL routing diff --git a/ROADMAP.adoc b/ROADMAP.adoc index d9259148..d8be8252 100644 --- a/ROADMAP.adoc +++ b/ROADMAP.adoc @@ -95,12 +95,10 @@ Each major version below lists explicit remedies for these concerns. * ✅ Cross-registry resolution * ✅ Lockfile system with integrity checks -**Trust Pipeline (5 Microservices):** -* ✅ claim-forge: Attestation generation +**Trust Pipeline (3 Microservices):** * ✅ checky-monkey: Package verification (async polling) * ✅ palimpsest-license: License analysis * ✅ oikos: 8-dimensional sustainability scoring -* ✅ cicd-hyper-a: Publication + federation **HAR Integration (3 Agents):** * ✅ github-search.sh: GitHub API search @@ -252,7 +250,7 @@ Each major version below lists explicit remedies for these concerns. **Status:** ✅ Released February 5, 2026 -**Achievement:** Container security pipeline with 4 Rust microservices for vulnerability scanning, image signing, policy verification, and runtime monitoring. +**Achievement:** Container security pipeline with 3 Rust microservices for vulnerability scanning, image signing, and policy verification. **Focus:** Container security, real-world testing, performance optimization, mobile completion @@ -279,13 +277,6 @@ Each major version below lists explicit remedies for these concerns. * ✅ Security violation reporting with severity * ✅ Tested with secure/insecure containers -**cerro-torre - Security Monitoring Service (Port 8088):** -* ✅ Falco integration (with simulated mode) -* ✅ Event buffer (10,000 events) -* ✅ Severity classification (EMERGENCY to DEBUG) -* ✅ Real-time metrics and event queries -* ✅ Event simulation for testing - **Integration & Testing:** * ✅ End-to-end pipeline testing * ✅ Mobile API integration (Phoenix backend) diff --git a/TEST-NEEDS.md b/TEST-NEEDS.md index 7bd78b61..bcf28f64 100644 --- a/TEST-NEEDS.md +++ b/TEST-NEEDS.md @@ -111,13 +111,11 @@ _(Previous: C, achieved 2026-04-04)_ - [x] Degraded workspace: one member unreachable; others succeed (3 tests) ### Trust Pipeline Live-Service E2E ✓ (`test/integration/trust_pipeline_live_e2e_test.exs`) -- [x] Health checks: all 5 services (claim-forge, checky-monkey, palimpsest, cicd-hyper-a, oikos) -- [x] Attestation generation + verification via claim-forge +- [x] Health checks: all 3 services (checky-monkey, palimpsest, oikos) - [x] Package verification via checky-monkey (async polling) - [x] Licence analysis via palimpsest-license -- [x] CI/CD gate via cicd-hyper-a - [x] Sustainability scoring via oikos -- [x] Full pipeline: coordinated call across all 5 services +- [x] Full pipeline: coordinated call across all 3 services - [x] 23 tests total; `@moduletag :live_service`; included in `trust-pipeline-e2e.yml` ### Property-Based Fuzz Harness ✓ (`test/opsm/fuzz_harness_test.exs`) diff --git a/TESTING.md b/TESTING.md index eb8e56d4..2b2e8e68 100644 --- a/TESTING.md +++ b/TESTING.md @@ -302,10 +302,8 @@ Test trust service health checks: opsm status # Should show status of: -# - claim-forge # - checky-monkey # - palimpsest-license -# - cicd-hyper-a # - oikos ``` @@ -349,9 +347,8 @@ opsm publish . # Expected output: # ✓ Manifest ingested -# ✓ Attestation generated (claim-forge) # ✓ License compatibility (palimpsest) -# ✓ Published to registry (cicd-hyper-a) +# ✓ Sustainability scored (oikos) # ⏳ Checky-monkey verification queued ``` diff --git a/TOPOLOGY.md b/TOPOLOGY.md index 743ea16c..3b5be1a1 100644 --- a/TOPOLOGY.md +++ b/TOPOLOGY.md @@ -23,9 +23,9 @@ Copyright (c) Jonathan D.A. Jewell │ │ ▼ ▼ ┌───────────────────────┐ ┌────────────────────────────────┐ - │ REGISTRY ADAPTERS(101)│ │ TRUST PIPELINE (5 SVCS) │ - │ - npm, Hex, Crates │ │ - claim-forge (Attest) │ - │ - PyPI, RubyGems, Go │ │ - checky-monkey (Verify) │ + │ REGISTRY ADAPTERS(101)│ │ TRUST PIPELINE (3 SVCS) │ + │ - npm, Hex, Crates │ │ - checky-monkey (Verify) │ + │ - PyPI, RubyGems, Go │ │ - palimpsest-license (License) │ │ - Agentic (HAR) │ │ - oikos (Sustainability) │ └──────────┬────────────┘ └──────────┬─────────────────────┘ │ │ @@ -35,7 +35,7 @@ Copyright (c) Jonathan D.A. Jewell │ SECURITY & PROOFS │ │ ┌───────────┐ ┌───────────────────┐ │ │ │ Rust NIF │ │ Container Sec │ │ - │ │ (PQ Crypto)│ │ (4 Microservices)│ │ + │ │ (PQ Crypto)│ │ (3 Microservices)│ │ │ └─────┬─────┘ └────────┬──────────┘ │ └────────│─────────────────│──────────────┘ │ │ @@ -64,8 +64,8 @@ CORE MANAGER (v2.0.0) Agentic Discovery (HAR) ██████████ 100% 3 agents verified TRUST & SECURITY - Trust Pipeline (5 Svcs) ██████████ 100% Integrity/License/Eco checks - Container Sec (4 Svcs) ██████████ 100% Scan/Sign/Verify/Monitor stable + Trust Pipeline (3 Svcs) ██████████ 100% Integrity/License/Eco checks + Container Sec (3 Svcs) ██████████ 100% Scan/Sign/Verify stable PQ Crypto (Rust NIF) ██████████ 100% Dilithium5/Kyber hybrid active Verified.Library ██████████ 100% SSRF/DoS prevention proven diff --git a/cli/Config.res b/cli/Config.res index 1084625d..344ed288 100644 --- a/cli/Config.res +++ b/cli/Config.res @@ -63,10 +63,8 @@ let defaultServiceConfig = (port: int): serviceConfig => { let exampleConfig = (): opsmConfig => { http: defaultHttpConfig, - claimForge: defaultServiceConfig(7001), checkyMonkey: defaultServiceConfig(7002), palimpsestLicense: defaultServiceConfig(7003), - cicdHyperA: defaultServiceConfig(7004), oikos: defaultServiceConfig(7005), } @@ -181,12 +179,6 @@ let loadConfigFrom = async (path: string): result => { let http = parseHttpConfig(raw) - let claimForge = switch parseServiceConfig(raw, "claim_forge", 7001) { - | Ok(c) => c - | Error(e) => // Can't return early in ReScript, so we'll use a default and check later - throw(JsError.throwWithMessage(e)) - } - let checkyMonkey = switch parseServiceConfig(raw, "checky_monkey", 7002) { | Ok(c) => c | Error(e) => throw(JsError.throwWithMessage(e)) @@ -197,11 +189,6 @@ let loadConfigFrom = async (path: string): result => { | Error(e) => throw(JsError.throwWithMessage(e)) } - let cicdHyperA = switch parseServiceConfig(raw, "cicd_hyper_a", 7004) { - | Ok(c) => c - | Error(e) => throw(JsError.throwWithMessage(e)) - } - let oikos = switch parseServiceConfig(raw, "oikos", 7005) { | Ok(c) => c | Error(e) => throw(JsError.throwWithMessage(e)) @@ -209,10 +196,8 @@ let loadConfigFrom = async (path: string): result => { Ok({ http, - claimForge, checkyMonkey, palimpsestLicense, - cicdHyperA, oikos, }) } catch { diff --git a/cli/Config.res.js b/cli/Config.res.js index 00b5ab66..6c96bfb8 100644 --- a/cli/Config.res.js +++ b/cli/Config.res.js @@ -43,10 +43,8 @@ function defaultServiceConfig(port) { function exampleConfig() { return { http: defaultHttpConfig, - claimForge: defaultServiceConfig(7001), checkyMonkey: defaultServiceConfig(7002), palimpsestLicense: defaultServiceConfig(7003), - cicdHyperA: defaultServiceConfig(7004), oikos: defaultServiceConfig(7005) }; } @@ -149,13 +147,6 @@ async function loadConfigFrom(path) { let data = await Deno.readTextFile(path); let raw = Toml.parse(data); let http = parseHttpConfig(raw); - let c = parseServiceConfig(raw, "claim_forge", 7001); - let claimForge; - if (c.TAG === "Ok") { - claimForge = c._0; - } else { - throw Stdlib_JsError.throwWithMessage(c._0); - } let c$1 = parseServiceConfig(raw, "checky_monkey", 7002); let checkyMonkey; if (c$1.TAG === "Ok") { @@ -170,13 +161,6 @@ async function loadConfigFrom(path) { } else { throw Stdlib_JsError.throwWithMessage(c$2._0); } - let c$3 = parseServiceConfig(raw, "cicd_hyper_a", 7004); - let cicdHyperA; - if (c$3.TAG === "Ok") { - cicdHyperA = c$3._0; - } else { - throw Stdlib_JsError.throwWithMessage(c$3._0); - } let c$4 = parseServiceConfig(raw, "oikos", 7005); let oikos; if (c$4.TAG === "Ok") { @@ -188,10 +172,8 @@ async function loadConfigFrom(path) { TAG: "Ok", _0: { http: http, - claimForge: claimForge, checkyMonkey: checkyMonkey, palimpsestLicense: palimpsestLicense, - cicdHyperA: cicdHyperA, oikos: oikos } }; diff --git a/cli/Types.res b/cli/Types.res index aad19ab0..0ee1842f 100644 --- a/cli/Types.res +++ b/cli/Types.res @@ -34,10 +34,8 @@ type serviceConfig = { type opsmConfig = { http: httpConfig, - claimForge: serviceConfig, checkyMonkey: serviceConfig, palimpsestLicense: serviceConfig, - cicdHyperA: serviceConfig, oikos: serviceConfig, } @@ -117,7 +115,8 @@ type oikosHealthResponse = { } // ============================================================================= -// CICD-HYPER-A - Registry and CI/CD Pipeline Hub +// Registry / Package Metadata +// (cicd-hyper-a service removed; publish/federation types deleted) // ============================================================================= type packageMetadata = { @@ -132,7 +131,7 @@ type packageMetadata = { devDependencies: option>, } -type attestationType = ClaimForge | Sigstore | InToto +type attestationType = Sigstore | InToto type attestationRef = { attestationType: attestationType, @@ -140,34 +139,6 @@ type attestationRef = { digest: string, } -type cicdPublishRequest = { - manifest: packageMetadata, - tarballUrl: option, - attestations: array, -} - -type syncState = { - synced: bool, - lastSync: option, - error: option, -} - -type federationStatus = { - github: syncState, - gitlab: syncState, - codeberg: syncState, - radicle: syncState, - ipfs: option, -} - -type cicdPublishResponse = { - packageId: string, - version: string, - publishedAt: string, - registryUrl: string, - federationStatus: federationStatus, -} - type ruleSeverity = RuleError | RuleWarning | RuleInfo type rule = { @@ -200,47 +171,6 @@ type packageQueryResponse = { downloads: int, } -// ============================================================================= -// CLAIM-FORGE - Attestation Generation -// ============================================================================= - -type claimType = - | BuildProvenance - | SourceAttestation - | VulnerabilityScan - | LicenseCompliance - | CodeReview - -type claimForgeRequest = { - artifactPath: string, - artifactDigest: string, - claimType: claimType, - metadata: option>, -} - -type claimForgeResponse = { - attestationId: string, - claimType: claimType, - createdAt: string, - expiresAt: option, - attestationUri: string, - signature: string, - publicKeyId: string, -} - -type claimVerifyRequest = { - attestationUri: string, - artifactDigest: string, -} - -type claimVerifyResponse = { - valid: bool, - claimType: claimType, - issuer: string, - issuedAt: string, - errors: option>, -} - // ============================================================================= // CHECKY-MONKEY - Code Verification // ============================================================================= diff --git a/cli/Wiring.res b/cli/Wiring.res index b268ac20..49a2b365 100644 --- a/cli/Wiring.res +++ b/cli/Wiring.res @@ -15,47 +15,7 @@ let runPublish = async (config: opsmConfig, path: string): result => { Console.log(`Publishing package from: ${path}`) - // Step 1: Generate attestation via claim-forge - Console.log(" → Generating attestation via claim-forge...") - let claimForgeClient = ClaimForge.make( - config.claimForge.baseUrl, - config.claimForge.token, - httpOpts, - ) - - let attestResult = await claimForgeClient->ClaimForge.attest({ - artifactPath: path, - artifactDigest: "sha256:placeholder", - claimType: BuildProvenance, - metadata: None, - }) - - switch attestResult { - | Error(e) => Console.log(` ✗ Attestation failed: ${e}`) - | Ok(_) => Console.log(" ✓ Attestation generated") - } - - // Step 2: Verify with checky-monkey - Console.log(" → Running verification via checky-monkey...") - let checkyClient = CheckyMonkey.make( - config.checkyMonkey.baseUrl, - config.checkyMonkey.token, - httpOpts, - ) - - let verifyResult = await checkyClient->CheckyMonkey.submitVerification({ - repositoryUrl: path, - commitSha: "HEAD", - verificationTypes: [PropertyTests, TypeChecking], - timeout: Some(300), - }) - - switch verifyResult { - | Error(e) => Console.log(` ✗ Verification failed: ${e}`) - | Ok(resp) => Console.log(` ✓ Verification submitted: ${resp.requestId}`) - } - - // Step 3: Check license compliance + // Step 1: Check license compliance via palimpsest-license Console.log(" → Checking license compliance via palimpsest-license...") let palimpsestClient = Palimpsest.make( config.palimpsestLicense.baseUrl, @@ -79,33 +39,28 @@ let runPublish = async (config: opsmConfig, path: string): result => { } } - // Step 4: Publish to registry - Console.log(" → Publishing to registry via cicd-hyper-a...") - let cicdClient = CicdHyperA.make(config.cicdHyperA.baseUrl, config.cicdHyperA.token, httpOpts) - - let publishResult = await cicdClient->CicdHyperA.publish({ - manifest: { - name: path, - version: "0.1.0", - description: None, - license: "PMPL-1.0", - repository: None, - authors: [], - keywords: [], - dependencies: Dict.make(), - devDependencies: None, - }, - tarballUrl: None, - attestations: [], + // Step 2: Verify with checky-monkey + Console.log(" → Running verification via checky-monkey...") + let checkyClient = CheckyMonkey.make( + config.checkyMonkey.baseUrl, + config.checkyMonkey.token, + httpOpts, + ) + + let verifyResult = await checkyClient->CheckyMonkey.submitVerification({ + repositoryUrl: path, + commitSha: "HEAD", + verificationTypes: [PropertyTests, TypeChecking], + timeout: Some(300), }) - switch publishResult { + switch verifyResult { | Error(e) => { - Console.log(` ✗ Publish failed: ${e}`) + Console.log(` ✗ Verification failed: ${e}`) Error(e) } | Ok(resp) => { - Console.log(` ✓ Published: ${resp.packageId}`) + Console.log(` ✓ Verification submitted: ${resp.requestId}`) Ok() } } @@ -220,10 +175,8 @@ let runStatus = async (config: opsmConfig): result => { Console.log("") Console.log("Configuration") Console.log("-------------") - Console.log(`claim-forge: ${config.claimForge.baseUrl}`) Console.log(`checky-monkey: ${config.checkyMonkey.baseUrl}`) Console.log(`palimpsest-license: ${config.palimpsestLicense.baseUrl}`) - Console.log(`cicd-hyper-a: ${config.cicdHyperA.baseUrl}`) Console.log(`oikos: ${config.oikos.baseUrl}`) Ok() diff --git a/cli/Wiring.res.js b/cli/Wiring.res.js index a4299d69..2c35ea5d 100644 --- a/cli/Wiring.res.js +++ b/cli/Wiring.res.js @@ -3,8 +3,6 @@ // Generated by ReScript, PLEASE EDIT WITH CARE import * as Oikos from "./clients/Oikos.res.js"; -import * as CicdHyperA from "./clients/CicdHyperA.res.js"; -import * as ClaimForge from "./clients/ClaimForge.res.js"; import * as Palimpsest from "./clients/Palimpsest.res.js"; import * as CheckyMonkey from "./clients/CheckyMonkey.res.js"; @@ -18,35 +16,6 @@ async function runPublish(config, path) { backoffMs: httpOpts_backoffMs }; console.log(`Publishing package from: ` + path); - console.log(" → Generating attestation via claim-forge..."); - let claimForgeClient = ClaimForge.make(config.claimForge.baseUrl, config.claimForge.token, httpOpts); - let attestResult = await ClaimForge.attest(claimForgeClient, { - artifactPath: path, - artifactDigest: "sha256:placeholder", - claimType: "BuildProvenance", - metadata: undefined - }); - if (attestResult.TAG === "Ok") { - console.log(" ✓ Attestation generated"); - } else { - console.log(` ✗ Attestation failed: ` + attestResult._0); - } - console.log(" → Running verification via checky-monkey..."); - let checkyClient = CheckyMonkey.make(config.checkyMonkey.baseUrl, config.checkyMonkey.token, httpOpts); - let verifyResult = await CheckyMonkey.submitVerification(checkyClient, { - repositoryUrl: path, - commitSha: "HEAD", - verificationTypes: [ - "PropertyTests", - "TypeChecking" - ], - timeout: 300 - }); - if (verifyResult.TAG === "Ok") { - console.log(` ✓ Verification submitted: ` + verifyResult._0.requestId); - } else { - console.log(` ✗ Verification failed: ` + verifyResult._0); - } console.log(" → Checking license compliance via palimpsest-license..."); let palimpsestClient = Palimpsest.make(config.palimpsestLicense.baseUrl, config.palimpsestLicense.token, httpOpts); let licenseResult = await Palimpsest.analyze(palimpsestClient, { @@ -63,32 +32,26 @@ async function runPublish(config, path) { } else { console.log(` ✗ License check failed: ` + licenseResult._0); } - console.log(" → Publishing to registry via cicd-hyper-a..."); - let cicdClient = CicdHyperA.make(config.cicdHyperA.baseUrl, config.cicdHyperA.token, httpOpts); - let publishResult = await CicdHyperA.publish(cicdClient, { - manifest: { - name: path, - version: "0.1.0", - description: undefined, - license: "PMPL-1.0", - repository: undefined, - authors: [], - keywords: [], - dependencies: {}, - devDependencies: undefined - }, - tarballUrl: undefined, - attestations: [] + console.log(" → Running verification via checky-monkey..."); + let checkyClient = CheckyMonkey.make(config.checkyMonkey.baseUrl, config.checkyMonkey.token, httpOpts); + let verifyResult = await CheckyMonkey.submitVerification(checkyClient, { + repositoryUrl: path, + commitSha: "HEAD", + verificationTypes: [ + "PropertyTests", + "TypeChecking" + ], + timeout: 300 }); - if (publishResult.TAG === "Ok") { - console.log(` ✓ Published: ` + publishResult._0.packageId); + if (verifyResult.TAG === "Ok") { + console.log(` ✓ Verification submitted: ` + verifyResult._0.requestId); return { TAG: "Ok", _0: undefined }; } - let e = publishResult._0; - console.log(` ✗ Publish failed: ` + e); + let e = verifyResult._0; + console.log(` ✗ Verification failed: ` + e); return { TAG: "Error", _0: e @@ -197,10 +160,8 @@ async function runStatus(config) { console.log(""); console.log("Configuration"); console.log("-------------"); - console.log(`claim-forge: ` + config.claimForge.baseUrl); console.log(`checky-monkey: ` + config.checkyMonkey.baseUrl); console.log(`palimpsest-license: ` + config.palimpsestLicense.baseUrl); - console.log(`cicd-hyper-a: ` + config.cicdHyperA.baseUrl); console.log(`oikos: ` + config.oikos.baseUrl); return { TAG: "Ok", diff --git a/cli/clients/CicdHyperA.res b/cli/clients/CicdHyperA.res deleted file mode 100644 index 66204275..00000000 --- a/cli/clients/CicdHyperA.res +++ /dev/null @@ -1,249 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -@@warning("-44-45") -open Types - -// ============================================================================= -// Client Type -// ============================================================================= - -type t = { - baseUrl: string, - token: option, - opts: httpOptions, -} - -let make = (baseUrl: string, token: option, opts: httpOptions): t => { - baseUrl, - token, - opts, -} - -// ============================================================================= -// Validation Types -// ============================================================================= - -type validationError = { - ruleId: string, - rulesetId: string, - message: string, - path: option, -} - -type validationWarning = { - ruleId: string, - rulesetId: string, - message: string, - path: option, -} - -type validationResult = { - valid: bool, - errors: array, - warnings: array, -} - -type federationStatusResponse = { - packageName: string, - version: string, - syncedAt: string, - targets: Dict.t, -} - -// ============================================================================= -// JSON Encoding -// ============================================================================= - -let encodeAttestationType = (t: attestationType): string => { - switch t { - | ClaimForge => "claim-forge" - | Sigstore => "sigstore" - | InToto => "in-toto" - } -} - -let encodeAttestationRef = (ref: attestationRef): Dict.t => { - let obj = Dict.make() - obj->Dict.set("type", ref.attestationType->encodeAttestationType->JSON.Encode.string) - obj->Dict.set("uri", ref.uri->JSON.Encode.string) - obj->Dict.set("digest", ref.digest->JSON.Encode.string) - obj -} - -let encodePackageMetadata = (m: packageMetadata): Dict.t => { - let obj = Dict.make() - obj->Dict.set("name", m.name->JSON.Encode.string) - obj->Dict.set("version", m.version->JSON.Encode.string) - switch m.description { - | Some(d) => obj->Dict.set("description", d->JSON.Encode.string) - | None => () - } - obj->Dict.set("license", m.license->JSON.Encode.string) - switch m.repository { - | Some(r) => obj->Dict.set("repository", r->JSON.Encode.string) - | None => () - } - obj->Dict.set("authors", m.authors->Array.map(JSON.Encode.string)->JSON.Encode.array) - obj->Dict.set("keywords", m.keywords->Array.map(JSON.Encode.string)->JSON.Encode.array) - obj -} - -let encodePublishRequest = (req: cicdPublishRequest): string => { - let obj = Dict.make() - obj->Dict.set("manifest", req.manifest->encodePackageMetadata->JSON.Encode.object) - switch req.tarballUrl { - | Some(url) => obj->Dict.set("tarballUrl", url->JSON.Encode.string) - | None => () - } - obj->Dict.set( - "attestations", - req.attestations - ->Array.map(a => a->encodeAttestationRef->JSON.Encode.object) - ->JSON.Encode.array, - ) - obj->JSON.Encode.object->JSON.stringify -} - -let encodeQueryRequest = (req: packageQueryRequest): string => { - let versionPart = switch req.version { - | Some(v) => `/${v}` - | None => "" - } - let queryParams = switch req.includeScores { - | Some(true) => "?includeScores=true" - | _ => "" - } - `${req.name->encodeURIComponent}${versionPart}${queryParams}` -} - -// ============================================================================= -// API Methods -// ============================================================================= - -let publish = async (client: t, request: cicdPublishRequest): result => { - let body = encodePublishRequest(request) - - switch await Http.postJson(client.baseUrl, "/packages/publish", client.token, body, client.opts) { - | Error(e) => Error(e) - | Ok(_) => { - let name = request.manifest.name->encodeURIComponent - let version = request.manifest.version - switch await Http.getJson( - client.baseUrl, - `/packages/${name}/${version}`, - client.token, - client.opts, - ) { - | Error(e) => Error(e) - | Ok(_json) => - // Parse response - simplified for now - Ok({ - packageId: `${request.manifest.name}@${request.manifest.version}`, - version: request.manifest.version, - publishedAt: "", - registryUrl: `${client.baseUrl}/packages/${name}/${version}`, - federationStatus: { - github: {synced: false, lastSync: None, error: None}, - gitlab: {synced: false, lastSync: None, error: None}, - codeberg: {synced: false, lastSync: None, error: None}, - radicle: {synced: false, lastSync: None, error: None}, - ipfs: None, - }, - }) - } - } - } -} - -let queryPackage = async (client: t, request: packageQueryRequest): result< - packageQueryResponse, -> => { - let path = `/packages/${encodeQueryRequest(request)}` - - switch await Http.getJson(client.baseUrl, path, client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => - // Parse response - simplified for now - Ok({ - package: { - name: request.name, - version: request.version->Option.getOr("0.0.0"), - description: None, - license: "PMPL-1.0", - repository: None, - authors: [], - keywords: [], - dependencies: Dict.make(), - devDependencies: None, - }, - versions: [], - latestVersion: "0.0.0", - scores: None, - dependents: 0, - downloads: 0, - }) - } -} - -let listRulesets = async (client: t): result> => { - switch await Http.getJson(client.baseUrl, "/rulesets", client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => Ok([]) - } -} - -let getRuleset = async (client: t, rulesetId: string): result => { - let path = `/rulesets/${rulesetId->encodeURIComponent}` - - switch await Http.getJson(client.baseUrl, path, client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => - Ok({ - id: rulesetId, - name: "", - version: "0.0.0", - rules: [], - appliesTo: [], - }) - } -} - -let validateManifest = async ( - client: t, - manifest: packageMetadata, - rulesetIds: option>, -): result => { - let obj = Dict.make() - obj->Dict.set("manifest", manifest->encodePackageMetadata->JSON.Encode.object) - obj->Dict.set( - "rulesetIds", - rulesetIds->Option.getOr(["default"])->Array.map(JSON.Encode.string)->JSON.Encode.array, - ) - let body = obj->JSON.Encode.object->JSON.stringify - - switch await Http.postJson(client.baseUrl, "/validate", client.token, body, client.opts) { - | Error(e) => Error(e) - | Ok(_) => - Ok({ - valid: true, - errors: [], - warnings: [], - }) - } -} - -let getFederationStatus = async (client: t, packageName: string, version: string): result< - federationStatusResponse, -> => { - let path = `/packages/${packageName->encodeURIComponent}/${version}/federation` - - switch await Http.getJson(client.baseUrl, path, client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => - Ok({ - packageName, - version, - syncedAt: "", - targets: Dict.make(), - }) - } -} diff --git a/cli/clients/CicdHyperA.res.js b/cli/clients/CicdHyperA.res.js deleted file mode 100644 index 58407dc5..00000000 --- a/cli/clients/CicdHyperA.res.js +++ /dev/null @@ -1,253 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright (c) Jonathan D.A. Jewell -// Generated by ReScript, PLEASE EDIT WITH CARE - -import * as Http from "../Http.res.js"; -import * as Core__Option from "@rescript/core/src/Core__Option.res.js"; - -function make(baseUrl, token, opts) { - return { - baseUrl: baseUrl, - token: token, - opts: opts - }; -} - -function encodeAttestationType(t) { - switch (t) { - case "ClaimForge" : - return "claim-forge"; - case "Sigstore" : - return "sigstore"; - case "InToto" : - return "in-toto"; - } -} - -function encodeAttestationRef(ref) { - let obj = {}; - obj["type"] = encodeAttestationType(ref.attestationType); - obj["uri"] = ref.uri; - obj["digest"] = ref.digest; - return obj; -} - -function encodePackageMetadata(m) { - let obj = {}; - obj["name"] = m.name; - obj["version"] = m.version; - let d = m.description; - if (d !== undefined) { - obj["description"] = d; - } - obj["license"] = m.license; - let r = m.repository; - if (r !== undefined) { - obj["repository"] = r; - } - obj["authors"] = m.authors.map(prim => prim); - obj["keywords"] = m.keywords.map(prim => prim); - return obj; -} - -function encodePublishRequest(req) { - let obj = {}; - obj["manifest"] = encodePackageMetadata(req.manifest); - let url = req.tarballUrl; - if (url !== undefined) { - obj["tarballUrl"] = url; - } - obj["attestations"] = req.attestations.map(encodeAttestationRef); - return JSON.stringify(obj); -} - -function encodeQueryRequest(req) { - let v = req.version; - let versionPart = v !== undefined ? `/` + v : ""; - let match = req.includeScores; - let queryParams = match !== undefined && match ? "?includeScores=true" : ""; - return encodeURIComponent(req.name) + versionPart + queryParams; -} - -async function publish(client, request) { - let body = encodePublishRequest(request); - let e = await Http.postJson(client.baseUrl, "/packages/publish", client.token, body, client.opts); - if (e.TAG !== "Ok") { - return { - TAG: "Error", - _0: e._0 - }; - } - let name = encodeURIComponent(request.manifest.name); - let version = request.manifest.version; - let e$1 = await Http.getJson(client.baseUrl, `/packages/` + name + `/` + version, client.token, client.opts); - if (e$1.TAG === "Ok") { - return { - TAG: "Ok", - _0: { - packageId: request.manifest.name + `@` + request.manifest.version, - version: request.manifest.version, - publishedAt: "", - registryUrl: client.baseUrl + `/packages/` + name + `/` + version, - federationStatus: { - github: { - synced: false, - lastSync: undefined, - error: undefined - }, - gitlab: { - synced: false, - lastSync: undefined, - error: undefined - }, - codeberg: { - synced: false, - lastSync: undefined, - error: undefined - }, - radicle: { - synced: false, - lastSync: undefined, - error: undefined - }, - ipfs: undefined - } - } - }; - } else { - return { - TAG: "Error", - _0: e$1._0 - }; - } -} - -async function queryPackage(client, request) { - let path = `/packages/` + encodeQueryRequest(request); - let e = await Http.getJson(client.baseUrl, path, client.token, client.opts); - if (e.TAG === "Ok") { - return { - TAG: "Ok", - _0: { - package: { - name: request.name, - version: Core__Option.getOr(request.version, "0.0.0"), - description: undefined, - license: "PMPL-1.0", - repository: undefined, - authors: [], - keywords: [], - dependencies: {}, - devDependencies: undefined - }, - versions: [], - latestVersion: "0.0.0", - scores: undefined, - dependents: 0, - downloads: 0 - } - }; - } else { - return { - TAG: "Error", - _0: e._0 - }; - } -} - -async function listRulesets(client) { - let e = await Http.getJson(client.baseUrl, "/rulesets", client.token, client.opts); - if (e.TAG === "Ok") { - return { - TAG: "Ok", - _0: [] - }; - } else { - return { - TAG: "Error", - _0: e._0 - }; - } -} - -async function getRuleset(client, rulesetId) { - let path = `/rulesets/` + encodeURIComponent(rulesetId); - let e = await Http.getJson(client.baseUrl, path, client.token, client.opts); - if (e.TAG === "Ok") { - return { - TAG: "Ok", - _0: { - id: rulesetId, - name: "", - version: "0.0.0", - rules: [], - appliesTo: [] - } - }; - } else { - return { - TAG: "Error", - _0: e._0 - }; - } -} - -async function validateManifest(client, manifest, rulesetIds) { - let obj = {}; - obj["manifest"] = encodePackageMetadata(manifest); - obj["rulesetIds"] = Core__Option.getOr(rulesetIds, ["default"]).map(prim => prim); - let body = JSON.stringify(obj); - let e = await Http.postJson(client.baseUrl, "/validate", client.token, body, client.opts); - if (e.TAG === "Ok") { - return { - TAG: "Ok", - _0: { - valid: true, - errors: [], - warnings: [] - } - }; - } else { - return { - TAG: "Error", - _0: e._0 - }; - } -} - -async function getFederationStatus(client, packageName, version) { - let path = `/packages/` + encodeURIComponent(packageName) + `/` + version + `/federation`; - let e = await Http.getJson(client.baseUrl, path, client.token, client.opts); - if (e.TAG === "Ok") { - return { - TAG: "Ok", - _0: { - packageName: packageName, - version: version, - syncedAt: "", - targets: {} - } - }; - } else { - return { - TAG: "Error", - _0: e._0 - }; - } -} - -export { - make, - encodeAttestationType, - encodeAttestationRef, - encodePackageMetadata, - encodePublishRequest, - encodeQueryRequest, - publish, - queryPackage, - listRulesets, - getRuleset, - validateManifest, - getFederationStatus, -} -/* Http Not a pure module */ diff --git a/cli/clients/ClaimForge.res b/cli/clients/ClaimForge.res deleted file mode 100644 index 3eb3e3ee..00000000 --- a/cli/clients/ClaimForge.res +++ /dev/null @@ -1,248 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -@@warning("-44-45") -open Types - -// ============================================================================= -// HTTP Client Type -// ============================================================================= - -type t = { - baseUrl: string, - token: option, - opts: httpOptions, -} - -let make = (baseUrl: string, token: option, opts: httpOptions): t => { - baseUrl, - token, - opts, -} - -// ============================================================================= -// Deno Command FFI for CLI fallback -// ============================================================================= - -module DenoCommand = { - type commandOptions = { - args: array, - stdout: string, - stderr: string, - } - - type commandOutput = { - code: int, - stdout: Uint8Array.t, - stderr: Uint8Array.t, - } - - type command - - @new @scope("Deno") external makeCommand: (string, commandOptions) => command = "Command" - @send external output: command => promise = "output" -} - -module TextDecoder = { - type t - - @new external make: unit => t = "TextDecoder" - @send external decode: (t, Uint8Array.t) => string = "decode" -} - -// ============================================================================= -// JSON Encoding -// ============================================================================= - -let encodeClaimType = (t: claimType): string => { - switch t { - | BuildProvenance => "build-provenance" - | SourceAttestation => "source-attestation" - | VulnerabilityScan => "vulnerability-scan" - | LicenseCompliance => "license-compliance" - | CodeReview => "code-review" - } -} - -let decodeClaimType = (s: string): claimType => { - switch s { - | "build-provenance" => BuildProvenance - | "source-attestation" => SourceAttestation - | "vulnerability-scan" => VulnerabilityScan - | "license-compliance" => LicenseCompliance - | "code-review" => CodeReview - | _ => BuildProvenance - } -} - -let encodeAttestRequest = (req: claimForgeRequest): string => { - let obj = Dict.make() - obj->Dict.set("artifactPath", req.artifactPath->JSON.Encode.string) - obj->Dict.set("artifactDigest", req.artifactDigest->JSON.Encode.string) - obj->Dict.set("claimType", req.claimType->encodeClaimType->JSON.Encode.string) - switch req.metadata { - | Some(m) => obj->Dict.set("metadata", m->JSON.Encode.object) - | None => () - } - obj->JSON.Encode.object->JSON.stringify -} - -let encodeVerifyRequest = (req: claimVerifyRequest): string => { - let obj = Dict.make() - obj->Dict.set("attestationUri", req.attestationUri->JSON.Encode.string) - obj->Dict.set("artifactDigest", req.artifactDigest->JSON.Encode.string) - obj->JSON.Encode.object->JSON.stringify -} - -// ============================================================================= -// HTTP API Methods -// ============================================================================= - -let attest = async (client: t, request: claimForgeRequest): result => { - let body = encodeAttestRequest(request) - - switch await Http.postJson(client.baseUrl, "/attest", client.token, body, client.opts) { - | Error(e) => Error(e) - | Ok(_) => { - let digest = request.artifactDigest->encodeURIComponent - switch await Http.getJson( - client.baseUrl, - `/attestations/${digest}`, - client.token, - client.opts, - ) { - | Error(e) => Error(e) - | Ok(_json) => - Ok({ - attestationId: "", - claimType: request.claimType, - createdAt: "", - expiresAt: None, - attestationUri: "", - signature: "", - publicKeyId: "", - }) - } - } - } -} - -let verify = async (client: t, request: claimVerifyRequest): result => { - let body = encodeVerifyRequest(request) - - switch await Http.postJson(client.baseUrl, "/verify", client.token, body, client.opts) { - | Error(e) => Error(e) - | Ok(_) => { - let uri = request.attestationUri->encodeURIComponent - switch await Http.getJson(client.baseUrl, `/verify/${uri}`, client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => - Ok({ - valid: true, - claimType: BuildProvenance, - issuer: "", - issuedAt: "", - errors: None, - }) - } - } - } -} - -let listClaimTypes = async (client: t): result> => { - switch await Http.getJson(client.baseUrl, "/claim-types", client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => - Ok([BuildProvenance, SourceAttestation, VulnerabilityScan, LicenseCompliance, CodeReview]) - } -} - -let getAttestation = async (client: t, attestationId: string): result => { - let path = `/attestations/${attestationId->encodeURIComponent}` - - switch await Http.getJson(client.baseUrl, path, client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => - Ok({ - attestationId, - claimType: BuildProvenance, - createdAt: "", - expiresAt: None, - attestationUri: "", - signature: "", - publicKeyId: "", - }) - } -} - -// ============================================================================= -// CLI Fallback -// ============================================================================= - -type cli = {binaryPath: string} - -let makeCli = (~binaryPath: string="claim-forge"): cli => { - binaryPath: binaryPath, -} - -let cliAttest = async ( - cli: cli, - artifactPath: string, - claimType: claimType, - outputPath: option, -): result => { - let args = ["attest", "--type", claimType->encodeClaimType, "--artifact", artifactPath] - let args = switch outputPath { - | Some(p) => args->Array.concat(["--output", p]) - | None => args - } - - try { - let cmd = DenoCommand.makeCommand( - cli.binaryPath, - { - args, - stdout: "piped", - stderr: "piped", - }, - ) - - let output = await cmd->DenoCommand.output - let decoder = TextDecoder.make() - - if output.code != 0 { - let errorText = decoder->TextDecoder.decode(output.stderr) - Error(`claim-forge failed: ${errorText}`) - } else { - let outputText = decoder->TextDecoder.decode(output.stdout) - Ok(outputText->String.trim) - } - } catch { - | JsExn(e) => Error(JsExn.message(e)->Option.getOr("Unknown error")) - } -} - -let cliVerify = async (cli: cli, attestationPath: string, artifactPath: string): result => { - let args = ["verify", "--attestation", attestationPath, "--artifact", artifactPath] - - try { - let cmd = DenoCommand.makeCommand( - cli.binaryPath, - { - args, - stdout: "piped", - stderr: "piped", - }, - ) - - let output = await cmd->DenoCommand.output - let decoder = TextDecoder.make() - - if output.code != 0 { - let errorText = decoder->TextDecoder.decode(output.stderr) - Error(`verification failed: ${errorText}`) - } else { - Ok(true) - } - } catch { - | JsExn(e) => Error(JsExn.message(e)->Option.getOr("Unknown error")) - } -} diff --git a/cli/clients/ClaimForge.res.js b/cli/clients/ClaimForge.res.js deleted file mode 100644 index 982b9f93..00000000 --- a/cli/clients/ClaimForge.res.js +++ /dev/null @@ -1,284 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright (c) Jonathan D.A. Jewell -// Generated by ReScript, PLEASE EDIT WITH CARE - -import * as Http from "../Http.res.js"; -import * as Core__Option from "@rescript/core/src/Core__Option.res.js"; -import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js"; -import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js"; - -function make(baseUrl, token, opts) { - return { - baseUrl: baseUrl, - token: token, - opts: opts - }; -} - -let DenoCommand = {}; - -let TextDecoder = {}; - -function encodeClaimType(t) { - switch (t) { - case "BuildProvenance" : - return "build-provenance"; - case "SourceAttestation" : - return "source-attestation"; - case "VulnerabilityScan" : - return "vulnerability-scan"; - case "LicenseCompliance" : - return "license-compliance"; - case "CodeReview" : - return "code-review"; - } -} - -function decodeClaimType(s) { - switch (s) { - case "code-review" : - return "CodeReview"; - case "license-compliance" : - return "LicenseCompliance"; - case "source-attestation" : - return "SourceAttestation"; - case "vulnerability-scan" : - return "VulnerabilityScan"; - default: - return "BuildProvenance"; - } -} - -function encodeAttestRequest(req) { - let obj = {}; - obj["artifactPath"] = req.artifactPath; - obj["artifactDigest"] = req.artifactDigest; - obj["claimType"] = encodeClaimType(req.claimType); - let m = req.metadata; - if (m !== undefined) { - obj["metadata"] = m; - } - return JSON.stringify(obj); -} - -function encodeVerifyRequest(req) { - let obj = {}; - obj["attestationUri"] = req.attestationUri; - obj["artifactDigest"] = req.artifactDigest; - return JSON.stringify(obj); -} - -async function attest(client, request) { - let body = encodeAttestRequest(request); - let e = await Http.postJson(client.baseUrl, "/attest", client.token, body, client.opts); - if (e.TAG !== "Ok") { - return { - TAG: "Error", - _0: e._0 - }; - } - let digest = encodeURIComponent(request.artifactDigest); - let e$1 = await Http.getJson(client.baseUrl, `/attestations/` + digest, client.token, client.opts); - if (e$1.TAG === "Ok") { - return { - TAG: "Ok", - _0: { - attestationId: "", - claimType: request.claimType, - createdAt: "", - expiresAt: undefined, - attestationUri: "", - signature: "", - publicKeyId: "" - } - }; - } else { - return { - TAG: "Error", - _0: e$1._0 - }; - } -} - -async function verify(client, request) { - let body = encodeVerifyRequest(request); - let e = await Http.postJson(client.baseUrl, "/verify", client.token, body, client.opts); - if (e.TAG !== "Ok") { - return { - TAG: "Error", - _0: e._0 - }; - } - let uri = encodeURIComponent(request.attestationUri); - let e$1 = await Http.getJson(client.baseUrl, `/verify/` + uri, client.token, client.opts); - if (e$1.TAG === "Ok") { - return { - TAG: "Ok", - _0: { - valid: true, - claimType: "BuildProvenance", - issuer: "", - issuedAt: "", - errors: undefined - } - }; - } else { - return { - TAG: "Error", - _0: e$1._0 - }; - } -} - -async function listClaimTypes(client) { - let e = await Http.getJson(client.baseUrl, "/claim-types", client.token, client.opts); - if (e.TAG === "Ok") { - return { - TAG: "Ok", - _0: [ - "BuildProvenance", - "SourceAttestation", - "VulnerabilityScan", - "LicenseCompliance", - "CodeReview" - ] - }; - } else { - return { - TAG: "Error", - _0: e._0 - }; - } -} - -async function getAttestation(client, attestationId) { - let path = `/attestations/` + encodeURIComponent(attestationId); - let e = await Http.getJson(client.baseUrl, path, client.token, client.opts); - if (e.TAG === "Ok") { - return { - TAG: "Ok", - _0: { - attestationId: attestationId, - claimType: "BuildProvenance", - createdAt: "", - expiresAt: undefined, - attestationUri: "", - signature: "", - publicKeyId: "" - } - }; - } else { - return { - TAG: "Error", - _0: e._0 - }; - } -} - -function makeCli(binaryPathOpt) { - let binaryPath = binaryPathOpt !== undefined ? binaryPathOpt : "claim-forge"; - return { - binaryPath: binaryPath - }; -} - -async function cliAttest(cli, artifactPath, claimType, outputPath) { - let args = [ - "attest", - "--type", - encodeClaimType(claimType), - "--artifact", - artifactPath - ]; - let args$1 = outputPath !== undefined ? args.concat([ - "--output", - outputPath - ]) : args; - try { - let cmd = new (Deno.Command)(cli.binaryPath, { - args: args$1, - stdout: "piped", - stderr: "piped" - }); - let output = await cmd.output(); - let decoder = new TextDecoder(); - if (output.code !== 0) { - let errorText = decoder.decode(output.stderr); - return { - TAG: "Error", - _0: `claim-forge failed: ` + errorText - }; - } - let outputText = decoder.decode(output.stdout); - return { - TAG: "Ok", - _0: outputText.trim() - }; - } catch (raw_e) { - let e = Primitive_exceptions.internalToException(raw_e); - if (e.RE_EXN_ID === "JsExn") { - return { - TAG: "Error", - _0: Core__Option.getOr(Stdlib_JsExn.message(e._1), "Unknown error") - }; - } - throw e; - } -} - -async function cliVerify(cli, attestationPath, artifactPath) { - let args = [ - "verify", - "--attestation", - attestationPath, - "--artifact", - artifactPath - ]; - try { - let cmd = new (Deno.Command)(cli.binaryPath, { - args: args, - stdout: "piped", - stderr: "piped" - }); - let output = await cmd.output(); - let decoder = new TextDecoder(); - if (output.code === 0) { - return { - TAG: "Ok", - _0: true - }; - } - let errorText = decoder.decode(output.stderr); - return { - TAG: "Error", - _0: `verification failed: ` + errorText - }; - } catch (raw_e) { - let e = Primitive_exceptions.internalToException(raw_e); - if (e.RE_EXN_ID === "JsExn") { - return { - TAG: "Error", - _0: Core__Option.getOr(Stdlib_JsExn.message(e._1), "Unknown error") - }; - } - throw e; - } -} - -export { - make, - DenoCommand, - TextDecoder, - encodeClaimType, - decodeClaimType, - encodeAttestRequest, - encodeVerifyRequest, - attest, - verify, - listClaimTypes, - getAttestation, - makeCli, - cliAttest, - cliVerify, -} -/* Http Not a pure module */ diff --git a/deploy/k8s/microservices.yaml b/deploy/k8s/microservices.yaml index a8250f1b..2a615c1e 100644 --- a/deploy/k8s/microservices.yaml +++ b/deploy/k8s/microservices.yaml @@ -1,78 +1,6 @@ # SPDX-License-Identifier: MPL-2.0 // Copyright (c) Jonathan D.A. Jewell -# Rust microservices: claim-forge, checky-monkey, palimpsest, cicd-hyper-a, oikos - ---- -# ========================================================================== -# Claim Forge — Attestation Generation -# ========================================================================== -apiVersion: apps/v1 -kind: Deployment -metadata: - name: claim-forge - namespace: opsm - labels: - app.kubernetes.io/name: claim-forge - app.kubernetes.io/component: attestation - app.kubernetes.io/part-of: opsm -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: claim-forge - template: - metadata: - labels: - app.kubernetes.io/name: claim-forge - app.kubernetes.io/component: attestation - app.kubernetes.io/part-of: opsm - spec: - automountServiceAccountToken: false - securityContext: - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 1000 - seccompProfile: - type: RuntimeDefault - containers: - - name: claim-forge - image: opsm-claim-forge:latest - ports: - - containerPort: 7001 - env: - - name: RUST_LOG - value: info - - name: BIND_ADDR - value: "0.0.0.0:7001" - resources: - requests: - memory: "64Mi" - cpu: "50m" - limits: - memory: "256Mi" - cpu: "250m" - livenessProbe: - httpGet: - path: /health - port: 7001 - periodSeconds: 30 - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - capabilities: - drop: ["ALL"] ---- -apiVersion: v1 -kind: Service -metadata: - name: claim-forge - namespace: opsm -spec: - selector: - app.kubernetes.io/name: claim-forge - ports: - - port: 7001 - targetPort: 7001 +# Rust microservices: checky-monkey, palimpsest, oikos --- # ========================================================================== @@ -218,78 +146,6 @@ spec: - port: 7003 targetPort: 7003 ---- -# ========================================================================== -# CICD-Hyper-A — Registry and CI/CD Hub -# ========================================================================== -apiVersion: apps/v1 -kind: Deployment -metadata: - name: cicd-hyper-a - namespace: opsm - labels: - app.kubernetes.io/name: cicd-hyper-a - app.kubernetes.io/component: cicd - app.kubernetes.io/part-of: opsm -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: cicd-hyper-a - template: - metadata: - labels: - app.kubernetes.io/name: cicd-hyper-a - app.kubernetes.io/component: cicd - app.kubernetes.io/part-of: opsm - spec: - automountServiceAccountToken: false - securityContext: - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 1000 - seccompProfile: - type: RuntimeDefault - containers: - - name: cicd-hyper-a - image: opsm-cicd-hyper-a:latest - ports: - - containerPort: 7004 - env: - - name: RUST_LOG - value: info - - name: BIND_ADDR - value: "0.0.0.0:7004" - resources: - requests: - memory: "64Mi" - cpu: "50m" - limits: - memory: "256Mi" - cpu: "250m" - livenessProbe: - httpGet: - path: /health - port: 7004 - periodSeconds: 30 - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - capabilities: - drop: ["ALL"] ---- -apiVersion: v1 -kind: Service -metadata: - name: cicd-hyper-a - namespace: opsm -spec: - selector: - app.kubernetes.io/name: cicd-hyper-a - ports: - - port: 7004 - targetPort: 7004 - --- # ========================================================================== # Oikos — Ecosystem Sustainability Analysis diff --git a/deploy/k8s/opsm-deployment.yaml b/deploy/k8s/opsm-deployment.yaml index ff4d8d10..460cd3c0 100644 --- a/deploy/k8s/opsm-deployment.yaml +++ b/deploy/k8s/opsm-deployment.yaml @@ -52,14 +52,10 @@ spec: env: - name: MIX_ENV value: prod - - name: OPSM_CLAIM_FORGE_URL - value: http://claim-forge:7001 - name: OPSM_CHECKY_MONKEY_URL value: http://checky-monkey:7002 - name: OPSM_PALIMPSEST_URL value: http://palimpsest:7003 - - name: OPSM_CICD_HYPER_A_URL - value: http://cicd-hyper-a:7004 - name: OPSM_OIKOS_URL value: http://oikos:7005 resources: diff --git a/deploy/selur-compose.yaml b/deploy/selur-compose.yaml index 447299d3..32317e5f 100644 --- a/deploy/selur-compose.yaml +++ b/deploy/selur-compose.yaml @@ -7,10 +7,8 @@ # # Services: # opsm - Core Elixir application (registry gateway + mobile API) -# claim-forge - Attestation generation (port 7001) # checky-monkey - Code verification (port 7002) # palimpsest - License analysis (port 7003) -# cicd-hyper-a - Registry and CI/CD hub (port 7004) # oikos - Ecosystem sustainability (port 7005) # svalinn - Edge gateway with policy enforcement (port 8443) @@ -32,10 +30,8 @@ services: - "4051:4051" # Mobile API environment: - MIX_ENV=prod - - OPSM_CLAIM_FORGE_URL=http://claim-forge:7001 - OPSM_CHECKY_MONKEY_URL=http://checky-monkey:7002 - OPSM_PALIMPSEST_URL=http://palimpsest:7003 - - OPSM_CICD_HYPER_A_URL=http://cicd-hyper-a:7004 - OPSM_OIKOS_URL=http://oikos:7005 volumes: - opsm-data:/app/data @@ -46,8 +42,6 @@ services: timeout: 3s retries: 3 depends_on: - claim-forge: - condition: service_healthy checky-monkey: condition: service_healthy networks: @@ -58,32 +52,6 @@ services: tmpfs: - /tmp - # ========================================================================== - # Microservice: Claim Forge (attestation generation) - # ========================================================================== - claim-forge: - build: - context: ../services/claim-forge - containerfile: Containerfile - image: opsm-claim-forge:latest - container_name: opsm-claim-forge - restart: unless-stopped - expose: - - "7001" - environment: - - RUST_LOG=info - - BIND_ADDR=0.0.0.0:7001 - healthcheck: - test: ["CMD", "/app/claim-forge", "health"] - interval: 30s - timeout: 3s - retries: 3 - networks: - - opsm-internal - security_opt: - - no-new-privileges:true - read_only: true - # ========================================================================== # Microservice: Checky Monkey (code verification) # ========================================================================== @@ -136,32 +104,6 @@ services: - no-new-privileges:true read_only: true - # ========================================================================== - # Microservice: CICD-Hyper-A (registry and CI/CD hub) - # ========================================================================== - cicd-hyper-a: - build: - context: ../services/cicd-hyper-a - containerfile: Containerfile - image: opsm-cicd-hyper-a:latest - container_name: opsm-cicd-hyper-a - restart: unless-stopped - expose: - - "7004" - environment: - - RUST_LOG=info - - BIND_ADDR=0.0.0.0:7004 - healthcheck: - test: ["CMD", "/app/cicd-hyper-a", "health"] - interval: 30s - timeout: 3s - retries: 3 - networks: - - opsm-internal - security_opt: - - no-new-privileges:true - read_only: true - # ========================================================================== # Microservice: Oikos (ecosystem sustainability analysis) # ========================================================================== diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 9834a394..83eccb2a 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -24,10 +24,8 @@ services: - OPSM_ENV=development - OPSM_API_PORT=4466 # Mock service URLs (services not yet implemented) - - CLAIM_FORGE_URL=http://localhost:8080 - CHECKY_MONKEY_URL=http://localhost:8081 - PALIMPSEST_URL=http://localhost:8082 - - CICD_HYPER_A_URL=http://localhost:8083 - OIKOS_URL=http://localhost:8084 volumes: - opsm-dev-state:/app/data diff --git a/docs/CLI-FEATURE-COMPARISON.md b/docs/CLI-FEATURE-COMPARISON.md index 28446095..ffdd44fa 100644 --- a/docs/CLI-FEATURE-COMPARISON.md +++ b/docs/CLI-FEATURE-COMPARISON.md @@ -256,14 +256,12 @@ opsm install my-app # Resolves across all registries ```bash opsm publish ./my-package # Automatically: -# 1. claim-forge: Generate attestation -# 2. checky-monkey: Verify signatures -# 3. palimpsest: Check licenses -# 4. oikos: Score sustainability (8 dimensions) -# 5. cicd-hyper-a: Publish with provenance +# 1. palimpsest: Check licenses +# 2. oikos: Score sustainability (8 dimensions) +# 3. checky-monkey: Verify signatures ``` -**Unique to OPSM:** 5-microservice verification before publish. +**Unique to OPSM:** multi-microservice verification before publish. ### 3. Formal Verification (v1.5) ✅✅ ```elixir diff --git a/docs/CONTAINER-INTEGRATION.md b/docs/CONTAINER-INTEGRATION.md index fddd065a..6425d537 100644 --- a/docs/CONTAINER-INTEGRATION.md +++ b/docs/CONTAINER-INTEGRATION.md @@ -18,7 +18,6 @@ OPSM provides end-to-end container security through integration with specialized │ 2. Scan → Svalinn (Trivy + Grype) │ │ 3. Sign → Selur (Cosign + Sigstore) │ │ 4. Verify → Vordr (OPA policies) │ -│ 5. Monitor → Cerro-Torre (eBPF + Falco) │ └─────────────────────────────────────────────────────┘ ``` @@ -61,15 +60,12 @@ just container-pipeline latest ghcr.io/hyperpolymath | Service | Port | Purpose | Technology | |---------|------|---------|------------| | **opsm** | 4466 | Main API/CLI | Elixir | -| **claim-forge** | 8080 | Attestation generation | Rust | | **checky-monkey** | 8081 | Code verification | Rust + AFL++ | | **palimpsest** | 8082 | License analysis | Gleam | -| **cicd-hyper-a** | 8083 | Package registry | Elixir | | **oikos** | 8084 | Sustainability analysis | Rust | | **svalinn** | 8085 | Vulnerability scanning | Trivy + Grype | | **selur** | 8086 | Image signing | Cosign + Sigstore | | **vordr** | 8087 | Runtime verification | OPA | -| **cerro-torre** | 8088 | Security monitoring | eBPF + Falco | ### Security Features @@ -94,8 +90,6 @@ just container-pipeline latest ghcr.io/hyperpolymath **Runtime Protection:** - OPA policy enforcement (Vordr) -- eBPF syscall monitoring (Cerro-Torre) -- Falco runtime security rules - Network segmentation (trust/public networks) ## CLI Usage @@ -108,7 +102,7 @@ opsm container build [--version ] # Examples opsm container build ./opsm_ex --version v1.0.1 -opsm container build ./services/claim-forge +opsm container build ./services/checky-monkey ``` ### Security Scanning @@ -228,7 +222,6 @@ export CONTAINER_REGISTRY=ghcr.io/hyperpolymath export SVALINN_URL=http://localhost:8085 export SELUR_URL=http://localhost:8086 export VORDR_URL=http://localhost:8087 -export CERRO_URL=http://localhost:8088 # Signing keys export SIGNING_KEY=/keys/signing.key @@ -450,16 +443,6 @@ docker run ghcr.io/hyperpolymath/my-package:latest ## Monitoring and Observability -### View Runtime Events (Cerro-Torre) - -```bash -# Stream security events -just compose-logs cerro-torre -f - -# Check for policy violations -curl http://localhost:8088/events?severity=high -``` - ### Audit Logs ```bash @@ -467,7 +450,7 @@ curl http://localhost:8088/events?severity=high cat /var/log/opsm/audit.log # Filter by service -grep "claim-forge" /var/log/opsm/audit.log +grep "checky-monkey" /var/log/opsm/audit.log ``` ### Health Checks diff --git a/docs/MOBILE-API.md b/docs/MOBILE-API.md index 5ed4c3af..36b8b4a3 100644 --- a/docs/MOBILE-API.md +++ b/docs/MOBILE-API.md @@ -381,12 +381,10 @@ JSON parsing uses `Opsm.Verified.Json` with limits: ### Trust Pipeline -Package installations go through 5-microservice verification: -1. **claim-forge** - Generate attestation -2. **checky-monkey** - Verify signatures -3. **palimpsest-license** - Check licenses -4. **oikos** - Score sustainability -5. **cicd-hyper-a** - Publish with provenance +Package installations go through multi-microservice verification: +1. **palimpsest-license** - Check licenses +2. **oikos** - Score sustainability +3. **checky-monkey** - Verify signatures ## Future Enhancements (v1.1+) diff --git a/docs/PAPER-SKETCH-zenodo-arxiv.md b/docs/PAPER-SKETCH-zenodo-arxiv.md index 78f6e8ea..9ea53939 100644 --- a/docs/PAPER-SKETCH-zenodo-arxiv.md +++ b/docs/PAPER-SKETCH-zenodo-arxiv.md @@ -84,14 +84,12 @@ propagated through a cross-ecosystem build graph to Eⱼ. - Property-based testing: ∀ inputs, PubGrub terminates and produces a minimal solution (QuickCheck / StreamData proofs) -4.3 **Trust Pipeline** (5 microservices) -- `claim-forge` (Rust): SLSA Level 3 provenance generation, ed25519+Dilithium5 signing +4.3 **Trust Pipeline** (3 microservices) - `checky-monkey` (Rust): tarball integrity, SBOM extraction, CycloneDX validation - `palimpsest-license` (Rust/Elixir): SPDX expression compatibility matrix, PMPL/MPL/AGPL/MIT/Apache interactions verified - `oikos` (Elixir): sustainability scoring — 8 dimensions (maintenance, bus factor, funding, responsiveness, test coverage, security posture, docs quality, governance) -- `cicd-hyper-a` (Rust): publication + federation, SLSA attestation anchoring 4.4 **Post-Quantum Hardening** - Motivation: harvest-now-decrypt-later against package signatures @@ -139,7 +137,7 @@ propagated through a cross-ecosystem build graph to Eⱼ. 6.4 **Security** - Supply chain simulation: inject a known-malicious package; OPSM detects via - claim-forge provenance mismatch in N% of cases + checky-monkey integrity mismatch in N% of cases ### §7 Limitations and Future Work - Resolver completeness for circular cross-ecosystem deps (currently rejected) @@ -162,7 +160,7 @@ propagated through a cross-ecosystem build graph to Eⱼ. | PubGrub correctly resolves cross-ecosystem deps where greedy fails | Failing test case + OPSM resolution | Needs construction | | Trust pipeline catches N% of SLSA-violating packages | Simulation or real-world corpus | Needs benchmark | | HAR reduces time-to-first-package for minority languages to < 1 day | Case studies (Pony? Lobster?) | Needs documentation | -| Post-quantum signing overhead < 2ms at p99 | Benchmark in claim-forge | Claimed; needs formal measurement | +| Post-quantum signing overhead < 2ms at p99 | Benchmark in the PQ signing NIF | Claimed; needs formal measurement | | License compatibility matrix is sound for SPDX core | Formal proof or exhaustive case analysis | Needs proof or scope statement | --- diff --git a/docs/architecture.adoc b/docs/architecture.adoc index 5d5a4a83..69a4b936 100644 --- a/docs/architecture.adoc +++ b/docs/architecture.adoc @@ -59,8 +59,8 @@ communication: - Verification: proven (Idris2) - Manifests: nickel-config-reporter + hybrid-automation-router + protocol-squisher + scaffoldia -- Trust pipeline: checky-monkey + claim-forge + palimpsest-license + oikos -- Registry: cicd-hyper-a + http-capability-gateway +- Trust pipeline: checky-monkey + palimpsest-license + oikos +- Registry: http-capability-gateway - Federation: git-private-farm + Radicle + IPFS - Bots: rhodibot, seambot, echidnabot, robot-repo-automaton diff --git a/docs/service-contracts.adoc b/docs/service-contracts.adoc index 7f50e52b..21e55101 100644 --- a/docs/service-contracts.adoc +++ b/docs/service-contracts.adoc @@ -9,7 +9,7 @@ This document describes the API contracts for the trust pipeline services that O == Overview -OPSM orchestrates five backend services to provide a complete package management experience with multi-dimensional trust scoring: +OPSM orchestrates a set of backend services to provide a complete package management experience with multi-dimensional trust scoring: [cols="1,2,1,1"] |=== @@ -20,16 +20,6 @@ OPSM orchestrates five backend services to provide a complete package management | Active | REST API -| *cicd-hyper-a* -| Registry and CI/CD pipeline hub -| Active -| REST API - -| *claim-forge* -| Attestation generation -| CLI-only -| CLI / Service wrapper - | *palimpsest-license* | License analysis | CLI-only @@ -112,136 +102,6 @@ Oikos provides 8-dimension sustainability scoring (0-100): } ---- -== CICD-Hyper-A - Registry and Pipeline Hub - -=== Endpoints - -[cols="1,2,3"] -|=== -| Method | Path | Description - -| POST | `/packages/publish` | Publish a package -| GET | `/packages/{name}` | Get package info -| GET | `/packages/{name}/{version}` | Get specific version -| POST | `/validate` | Validate manifest against rulesets -| GET | `/rulesets` | List available rulesets -| GET | `/rulesets/{id}` | Get specific ruleset -|=== - -=== Publish Request - -Publishing requires: -1. Valid package manifest -2. Attestations from claim-forge -3. Passing ruleset validation - -[source,json] ----- -{ - "manifest": { - "name": "@scope/package", - "version": "1.0.0", - "description": "Package description", - "license": "PMPL-1.0", - "repository": "https://github.com/org/repo", - "authors": ["Author Name"], - "keywords": ["opsm", "package"], - "dependencies": { - "@other/dep": "^2.0.0" - } - }, - "tarballUrl": "https://registry.opsm.dev/tarballs/...", - "attestations": [ - { - "type": "claim-forge", - "uri": "https://attestations.opsm.dev/...", - "digest": "sha256:..." - } - ] -} ----- - -=== Federation Status - -After publishing, packages are federated to: - -* GitHub (releases) -* GitLab (packages) -* Codeberg (releases) -* Radicle (P2P) -* IPFS (content-addressed) - -[source,json] ----- -{ - "packageName": "@scope/package", - "version": "1.0.0", - "syncedAt": "2026-01-18T12:00:00Z", - "targets": { - "github": { "synced": true, "url": "https://github.com/..." }, - "gitlab": { "synced": true, "url": "https://gitlab.com/..." }, - "codeberg": { "synced": true, "url": "https://codeberg.org/..." }, - "radicle": { "synced": true }, - "ipfs": { "synced": false, "error": "Gateway timeout" } - } -} ----- - -== Claim-Forge - Attestation Generation - -=== Overview - -Claim-forge generates cryptographic attestations for artifacts. The upstream implementation is an Ada CLI tool. OPSM supports two integration modes: - -1. *Service mode*: HTTP API via claim-forge-service wrapper -2. *CLI mode*: Direct invocation of claim-forge binary - -=== Claim Types - -* `build-provenance` - SLSA build attestation -* `source-attestation` - Source code verification -* `vulnerability-scan` - Security scan results -* `license-compliance` - License analysis attestation -* `code-review` - Code review attestation - -=== Service API - -[cols="1,2,3"] -|=== -| Method | Path | Description - -| POST | `/attest` | Generate attestation -| POST | `/verify` | Verify attestation -| GET | `/attestations/{id}` | Get attestation by ID -| GET | `/claim-types` | List supported claim types -|=== - -=== Attestation Request - -[source,json] ----- -{ - "artifactPath": "/path/to/artifact.tar.gz", - "artifactDigest": "sha256:...", - "claimType": "build-provenance", - "metadata": { - "builder": "deno", - "version": "1.40.0" - } -} ----- - -=== CLI Usage - -[source,bash] ----- -# Generate attestation -claim-forge attest --type build-provenance --artifact ./package.tar.gz --output ./attestation.json - -# Verify attestation -claim-forge verify --attestation ./attestation.json --artifact ./package.tar.gz ----- - == Checky-Monkey - Code Verification === Overview @@ -324,7 +184,7 @@ NOTE: The REST API is currently in developsment. This documents the planned cont === Overview -Palimpsest-license performs SPDX license detection and compatibility analysis. Like claim-forge, it's primarily a CLI/spec project with optional service wrapper. +Palimpsest-license performs SPDX license detection and compatibility analysis. It is primarily a CLI/spec project with optional service wrapper. === Service API @@ -442,8 +302,8 @@ Tokens are configured per-service in `opsm.toml`: [source,toml] ---- -[claim_forge] -base_url = "http://127.0.0.1:7001" +[oikos] +base_url = "http://127.0.0.1:7005" token = "your-token-here" ---- @@ -454,8 +314,6 @@ TypeScript clients are available in `cli/clients/`: [source,typescript] ---- import { OikosClient } from "./clients/oikos.ts"; -import { CicdHyperAClient } from "./clients/cicd_hyper_a.ts"; -import { ClaimForgeClient, ClaimForgeCli } from "./clients/claim_forge.ts"; import { CheckyMonkeyClient } from "./clients/checky_monkey.ts"; import { PalimpsestClient, PalimpsestCli } from "./clients/palimpsest_license.ts"; @@ -464,6 +322,6 @@ const oikos = new OikosClient(config.baseUrl, config.token, httpOpts); const result = await oikos.analyzeRepository({ repositoryUrl: "..." }); // CLI fallback for services without HTTP wrapper -const claimCli = new ClaimForgeCli("/usr/local/bin/claim-forge"); -const attestation = await claimCli.attest("./artifact.tar.gz", "build-provenance"); +const palimpsestCli = new PalimpsestCli("/usr/local/bin/palimpsest-license"); +const analysis = await palimpsestCli.analyze("./project"); ---- diff --git a/lib/ocaml/CicdHyperA.cmj b/lib/ocaml/CicdHyperA.cmj deleted file mode 100644 index 1a8a44f33d66b6e1630f446fcc2eb74306d8a19f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 378 zcmZXOy-veG5QHxsE$@I7DMSgv(4RrjMG6i|zu24ji2v}dk=)bJrJ*+swJSAG1s0Y_t F%3rYhm*D^a diff --git a/lib/ocaml/CicdHyperA.res b/lib/ocaml/CicdHyperA.res deleted file mode 100644 index 66204275..00000000 --- a/lib/ocaml/CicdHyperA.res +++ /dev/null @@ -1,249 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -@@warning("-44-45") -open Types - -// ============================================================================= -// Client Type -// ============================================================================= - -type t = { - baseUrl: string, - token: option, - opts: httpOptions, -} - -let make = (baseUrl: string, token: option, opts: httpOptions): t => { - baseUrl, - token, - opts, -} - -// ============================================================================= -// Validation Types -// ============================================================================= - -type validationError = { - ruleId: string, - rulesetId: string, - message: string, - path: option, -} - -type validationWarning = { - ruleId: string, - rulesetId: string, - message: string, - path: option, -} - -type validationResult = { - valid: bool, - errors: array, - warnings: array, -} - -type federationStatusResponse = { - packageName: string, - version: string, - syncedAt: string, - targets: Dict.t, -} - -// ============================================================================= -// JSON Encoding -// ============================================================================= - -let encodeAttestationType = (t: attestationType): string => { - switch t { - | ClaimForge => "claim-forge" - | Sigstore => "sigstore" - | InToto => "in-toto" - } -} - -let encodeAttestationRef = (ref: attestationRef): Dict.t => { - let obj = Dict.make() - obj->Dict.set("type", ref.attestationType->encodeAttestationType->JSON.Encode.string) - obj->Dict.set("uri", ref.uri->JSON.Encode.string) - obj->Dict.set("digest", ref.digest->JSON.Encode.string) - obj -} - -let encodePackageMetadata = (m: packageMetadata): Dict.t => { - let obj = Dict.make() - obj->Dict.set("name", m.name->JSON.Encode.string) - obj->Dict.set("version", m.version->JSON.Encode.string) - switch m.description { - | Some(d) => obj->Dict.set("description", d->JSON.Encode.string) - | None => () - } - obj->Dict.set("license", m.license->JSON.Encode.string) - switch m.repository { - | Some(r) => obj->Dict.set("repository", r->JSON.Encode.string) - | None => () - } - obj->Dict.set("authors", m.authors->Array.map(JSON.Encode.string)->JSON.Encode.array) - obj->Dict.set("keywords", m.keywords->Array.map(JSON.Encode.string)->JSON.Encode.array) - obj -} - -let encodePublishRequest = (req: cicdPublishRequest): string => { - let obj = Dict.make() - obj->Dict.set("manifest", req.manifest->encodePackageMetadata->JSON.Encode.object) - switch req.tarballUrl { - | Some(url) => obj->Dict.set("tarballUrl", url->JSON.Encode.string) - | None => () - } - obj->Dict.set( - "attestations", - req.attestations - ->Array.map(a => a->encodeAttestationRef->JSON.Encode.object) - ->JSON.Encode.array, - ) - obj->JSON.Encode.object->JSON.stringify -} - -let encodeQueryRequest = (req: packageQueryRequest): string => { - let versionPart = switch req.version { - | Some(v) => `/${v}` - | None => "" - } - let queryParams = switch req.includeScores { - | Some(true) => "?includeScores=true" - | _ => "" - } - `${req.name->encodeURIComponent}${versionPart}${queryParams}` -} - -// ============================================================================= -// API Methods -// ============================================================================= - -let publish = async (client: t, request: cicdPublishRequest): result => { - let body = encodePublishRequest(request) - - switch await Http.postJson(client.baseUrl, "/packages/publish", client.token, body, client.opts) { - | Error(e) => Error(e) - | Ok(_) => { - let name = request.manifest.name->encodeURIComponent - let version = request.manifest.version - switch await Http.getJson( - client.baseUrl, - `/packages/${name}/${version}`, - client.token, - client.opts, - ) { - | Error(e) => Error(e) - | Ok(_json) => - // Parse response - simplified for now - Ok({ - packageId: `${request.manifest.name}@${request.manifest.version}`, - version: request.manifest.version, - publishedAt: "", - registryUrl: `${client.baseUrl}/packages/${name}/${version}`, - federationStatus: { - github: {synced: false, lastSync: None, error: None}, - gitlab: {synced: false, lastSync: None, error: None}, - codeberg: {synced: false, lastSync: None, error: None}, - radicle: {synced: false, lastSync: None, error: None}, - ipfs: None, - }, - }) - } - } - } -} - -let queryPackage = async (client: t, request: packageQueryRequest): result< - packageQueryResponse, -> => { - let path = `/packages/${encodeQueryRequest(request)}` - - switch await Http.getJson(client.baseUrl, path, client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => - // Parse response - simplified for now - Ok({ - package: { - name: request.name, - version: request.version->Option.getOr("0.0.0"), - description: None, - license: "PMPL-1.0", - repository: None, - authors: [], - keywords: [], - dependencies: Dict.make(), - devDependencies: None, - }, - versions: [], - latestVersion: "0.0.0", - scores: None, - dependents: 0, - downloads: 0, - }) - } -} - -let listRulesets = async (client: t): result> => { - switch await Http.getJson(client.baseUrl, "/rulesets", client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => Ok([]) - } -} - -let getRuleset = async (client: t, rulesetId: string): result => { - let path = `/rulesets/${rulesetId->encodeURIComponent}` - - switch await Http.getJson(client.baseUrl, path, client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => - Ok({ - id: rulesetId, - name: "", - version: "0.0.0", - rules: [], - appliesTo: [], - }) - } -} - -let validateManifest = async ( - client: t, - manifest: packageMetadata, - rulesetIds: option>, -): result => { - let obj = Dict.make() - obj->Dict.set("manifest", manifest->encodePackageMetadata->JSON.Encode.object) - obj->Dict.set( - "rulesetIds", - rulesetIds->Option.getOr(["default"])->Array.map(JSON.Encode.string)->JSON.Encode.array, - ) - let body = obj->JSON.Encode.object->JSON.stringify - - switch await Http.postJson(client.baseUrl, "/validate", client.token, body, client.opts) { - | Error(e) => Error(e) - | Ok(_) => - Ok({ - valid: true, - errors: [], - warnings: [], - }) - } -} - -let getFederationStatus = async (client: t, packageName: string, version: string): result< - federationStatusResponse, -> => { - let path = `/packages/${packageName->encodeURIComponent}/${version}/federation` - - switch await Http.getJson(client.baseUrl, path, client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => - Ok({ - packageName, - version, - syncedAt: "", - targets: Dict.make(), - }) - } -} diff --git a/lib/ocaml/ClaimForge.cmj b/lib/ocaml/ClaimForge.cmj deleted file mode 100644 index 331fe64caddfb26b86907314b5df802adaf0bb7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 335 zcmWHK-|E}G@s(Qt=hq1iY77P~QyU|`f{U|?_s;`cy&^8g0}gE7MfmE6Sa)Cmg~ zI6F8vY*0%qDM>9Z0dk$d+_Kc7%(O}vR~@9(IVTgycLYgmCg)^2!Zf(RM8aTtz$WUY zr, - opts: httpOptions, -} - -let make = (baseUrl: string, token: option, opts: httpOptions): t => { - baseUrl, - token, - opts, -} - -// ============================================================================= -// Deno Command FFI for CLI fallback -// ============================================================================= - -module DenoCommand = { - type commandOptions = { - args: array, - stdout: string, - stderr: string, - } - - type commandOutput = { - code: int, - stdout: Uint8Array.t, - stderr: Uint8Array.t, - } - - type command - - @new @scope("Deno") external makeCommand: (string, commandOptions) => command = "Command" - @send external output: command => promise = "output" -} - -module TextDecoder = { - type t - - @new external make: unit => t = "TextDecoder" - @send external decode: (t, Uint8Array.t) => string = "decode" -} - -// ============================================================================= -// JSON Encoding -// ============================================================================= - -let encodeClaimType = (t: claimType): string => { - switch t { - | BuildProvenance => "build-provenance" - | SourceAttestation => "source-attestation" - | VulnerabilityScan => "vulnerability-scan" - | LicenseCompliance => "license-compliance" - | CodeReview => "code-review" - } -} - -let decodeClaimType = (s: string): claimType => { - switch s { - | "build-provenance" => BuildProvenance - | "source-attestation" => SourceAttestation - | "vulnerability-scan" => VulnerabilityScan - | "license-compliance" => LicenseCompliance - | "code-review" => CodeReview - | _ => BuildProvenance - } -} - -let encodeAttestRequest = (req: claimForgeRequest): string => { - let obj = Dict.make() - obj->Dict.set("artifactPath", req.artifactPath->JSON.Encode.string) - obj->Dict.set("artifactDigest", req.artifactDigest->JSON.Encode.string) - obj->Dict.set("claimType", req.claimType->encodeClaimType->JSON.Encode.string) - switch req.metadata { - | Some(m) => obj->Dict.set("metadata", m->JSON.Encode.object) - | None => () - } - obj->JSON.Encode.object->JSON.stringify -} - -let encodeVerifyRequest = (req: claimVerifyRequest): string => { - let obj = Dict.make() - obj->Dict.set("attestationUri", req.attestationUri->JSON.Encode.string) - obj->Dict.set("artifactDigest", req.artifactDigest->JSON.Encode.string) - obj->JSON.Encode.object->JSON.stringify -} - -// ============================================================================= -// HTTP API Methods -// ============================================================================= - -let attest = async (client: t, request: claimForgeRequest): result => { - let body = encodeAttestRequest(request) - - switch await Http.postJson(client.baseUrl, "/attest", client.token, body, client.opts) { - | Error(e) => Error(e) - | Ok(_) => { - let digest = request.artifactDigest->encodeURIComponent - switch await Http.getJson( - client.baseUrl, - `/attestations/${digest}`, - client.token, - client.opts, - ) { - | Error(e) => Error(e) - | Ok(_json) => - Ok({ - attestationId: "", - claimType: request.claimType, - createdAt: "", - expiresAt: None, - attestationUri: "", - signature: "", - publicKeyId: "", - }) - } - } - } -} - -let verify = async (client: t, request: claimVerifyRequest): result => { - let body = encodeVerifyRequest(request) - - switch await Http.postJson(client.baseUrl, "/verify", client.token, body, client.opts) { - | Error(e) => Error(e) - | Ok(_) => { - let uri = request.attestationUri->encodeURIComponent - switch await Http.getJson(client.baseUrl, `/verify/${uri}`, client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => - Ok({ - valid: true, - claimType: BuildProvenance, - issuer: "", - issuedAt: "", - errors: None, - }) - } - } - } -} - -let listClaimTypes = async (client: t): result> => { - switch await Http.getJson(client.baseUrl, "/claim-types", client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => - Ok([BuildProvenance, SourceAttestation, VulnerabilityScan, LicenseCompliance, CodeReview]) - } -} - -let getAttestation = async (client: t, attestationId: string): result => { - let path = `/attestations/${attestationId->encodeURIComponent}` - - switch await Http.getJson(client.baseUrl, path, client.token, client.opts) { - | Error(e) => Error(e) - | Ok(_json) => - Ok({ - attestationId, - claimType: BuildProvenance, - createdAt: "", - expiresAt: None, - attestationUri: "", - signature: "", - publicKeyId: "", - }) - } -} - -// ============================================================================= -// CLI Fallback -// ============================================================================= - -type cli = {binaryPath: string} - -let makeCli = (~binaryPath: string="claim-forge"): cli => { - binaryPath: binaryPath, -} - -let cliAttest = async ( - cli: cli, - artifactPath: string, - claimType: claimType, - outputPath: option, -): result => { - let args = ["attest", "--type", claimType->encodeClaimType, "--artifact", artifactPath] - let args = switch outputPath { - | Some(p) => args->Array.concat(["--output", p]) - | None => args - } - - try { - let cmd = DenoCommand.makeCommand( - cli.binaryPath, - { - args, - stdout: "piped", - stderr: "piped", - }, - ) - - let output = await cmd->DenoCommand.output - let decoder = TextDecoder.make() - - if output.code != 0 { - let errorText = decoder->TextDecoder.decode(output.stderr) - Error(`claim-forge failed: ${errorText}`) - } else { - let outputText = decoder->TextDecoder.decode(output.stdout) - Ok(outputText->String.trim) - } - } catch { - | JsExn(e) => Error(JsExn.message(e)->Option.getOr("Unknown error")) - } -} - -let cliVerify = async (cli: cli, attestationPath: string, artifactPath: string): result => { - let args = ["verify", "--attestation", attestationPath, "--artifact", artifactPath] - - try { - let cmd = DenoCommand.makeCommand( - cli.binaryPath, - { - args, - stdout: "piped", - stderr: "piped", - }, - ) - - let output = await cmd->DenoCommand.output - let decoder = TextDecoder.make() - - if output.code != 0 { - let errorText = decoder->TextDecoder.decode(output.stderr) - Error(`verification failed: ${errorText}`) - } else { - Ok(true) - } - } catch { - | JsExn(e) => Error(JsExn.message(e)->Option.getOr("Unknown error")) - } -} diff --git a/lib/ocaml/Config.res b/lib/ocaml/Config.res index 1084625d..344ed288 100644 --- a/lib/ocaml/Config.res +++ b/lib/ocaml/Config.res @@ -63,10 +63,8 @@ let defaultServiceConfig = (port: int): serviceConfig => { let exampleConfig = (): opsmConfig => { http: defaultHttpConfig, - claimForge: defaultServiceConfig(7001), checkyMonkey: defaultServiceConfig(7002), palimpsestLicense: defaultServiceConfig(7003), - cicdHyperA: defaultServiceConfig(7004), oikos: defaultServiceConfig(7005), } @@ -181,12 +179,6 @@ let loadConfigFrom = async (path: string): result => { let http = parseHttpConfig(raw) - let claimForge = switch parseServiceConfig(raw, "claim_forge", 7001) { - | Ok(c) => c - | Error(e) => // Can't return early in ReScript, so we'll use a default and check later - throw(JsError.throwWithMessage(e)) - } - let checkyMonkey = switch parseServiceConfig(raw, "checky_monkey", 7002) { | Ok(c) => c | Error(e) => throw(JsError.throwWithMessage(e)) @@ -197,11 +189,6 @@ let loadConfigFrom = async (path: string): result => { | Error(e) => throw(JsError.throwWithMessage(e)) } - let cicdHyperA = switch parseServiceConfig(raw, "cicd_hyper_a", 7004) { - | Ok(c) => c - | Error(e) => throw(JsError.throwWithMessage(e)) - } - let oikos = switch parseServiceConfig(raw, "oikos", 7005) { | Ok(c) => c | Error(e) => throw(JsError.throwWithMessage(e)) @@ -209,10 +196,8 @@ let loadConfigFrom = async (path: string): result => { Ok({ http, - claimForge, checkyMonkey, palimpsestLicense, - cicdHyperA, oikos, }) } catch { diff --git a/lib/ocaml/Types.res b/lib/ocaml/Types.res index aad19ab0..0ee1842f 100644 --- a/lib/ocaml/Types.res +++ b/lib/ocaml/Types.res @@ -34,10 +34,8 @@ type serviceConfig = { type opsmConfig = { http: httpConfig, - claimForge: serviceConfig, checkyMonkey: serviceConfig, palimpsestLicense: serviceConfig, - cicdHyperA: serviceConfig, oikos: serviceConfig, } @@ -117,7 +115,8 @@ type oikosHealthResponse = { } // ============================================================================= -// CICD-HYPER-A - Registry and CI/CD Pipeline Hub +// Registry / Package Metadata +// (cicd-hyper-a service removed; publish/federation types deleted) // ============================================================================= type packageMetadata = { @@ -132,7 +131,7 @@ type packageMetadata = { devDependencies: option>, } -type attestationType = ClaimForge | Sigstore | InToto +type attestationType = Sigstore | InToto type attestationRef = { attestationType: attestationType, @@ -140,34 +139,6 @@ type attestationRef = { digest: string, } -type cicdPublishRequest = { - manifest: packageMetadata, - tarballUrl: option, - attestations: array, -} - -type syncState = { - synced: bool, - lastSync: option, - error: option, -} - -type federationStatus = { - github: syncState, - gitlab: syncState, - codeberg: syncState, - radicle: syncState, - ipfs: option, -} - -type cicdPublishResponse = { - packageId: string, - version: string, - publishedAt: string, - registryUrl: string, - federationStatus: federationStatus, -} - type ruleSeverity = RuleError | RuleWarning | RuleInfo type rule = { @@ -200,47 +171,6 @@ type packageQueryResponse = { downloads: int, } -// ============================================================================= -// CLAIM-FORGE - Attestation Generation -// ============================================================================= - -type claimType = - | BuildProvenance - | SourceAttestation - | VulnerabilityScan - | LicenseCompliance - | CodeReview - -type claimForgeRequest = { - artifactPath: string, - artifactDigest: string, - claimType: claimType, - metadata: option>, -} - -type claimForgeResponse = { - attestationId: string, - claimType: claimType, - createdAt: string, - expiresAt: option, - attestationUri: string, - signature: string, - publicKeyId: string, -} - -type claimVerifyRequest = { - attestationUri: string, - artifactDigest: string, -} - -type claimVerifyResponse = { - valid: bool, - claimType: claimType, - issuer: string, - issuedAt: string, - errors: option>, -} - // ============================================================================= // CHECKY-MONKEY - Code Verification // ============================================================================= diff --git a/lib/ocaml/Wiring.res b/lib/ocaml/Wiring.res index b268ac20..49a2b365 100644 --- a/lib/ocaml/Wiring.res +++ b/lib/ocaml/Wiring.res @@ -15,47 +15,7 @@ let runPublish = async (config: opsmConfig, path: string): result => { Console.log(`Publishing package from: ${path}`) - // Step 1: Generate attestation via claim-forge - Console.log(" → Generating attestation via claim-forge...") - let claimForgeClient = ClaimForge.make( - config.claimForge.baseUrl, - config.claimForge.token, - httpOpts, - ) - - let attestResult = await claimForgeClient->ClaimForge.attest({ - artifactPath: path, - artifactDigest: "sha256:placeholder", - claimType: BuildProvenance, - metadata: None, - }) - - switch attestResult { - | Error(e) => Console.log(` ✗ Attestation failed: ${e}`) - | Ok(_) => Console.log(" ✓ Attestation generated") - } - - // Step 2: Verify with checky-monkey - Console.log(" → Running verification via checky-monkey...") - let checkyClient = CheckyMonkey.make( - config.checkyMonkey.baseUrl, - config.checkyMonkey.token, - httpOpts, - ) - - let verifyResult = await checkyClient->CheckyMonkey.submitVerification({ - repositoryUrl: path, - commitSha: "HEAD", - verificationTypes: [PropertyTests, TypeChecking], - timeout: Some(300), - }) - - switch verifyResult { - | Error(e) => Console.log(` ✗ Verification failed: ${e}`) - | Ok(resp) => Console.log(` ✓ Verification submitted: ${resp.requestId}`) - } - - // Step 3: Check license compliance + // Step 1: Check license compliance via palimpsest-license Console.log(" → Checking license compliance via palimpsest-license...") let palimpsestClient = Palimpsest.make( config.palimpsestLicense.baseUrl, @@ -79,33 +39,28 @@ let runPublish = async (config: opsmConfig, path: string): result => { } } - // Step 4: Publish to registry - Console.log(" → Publishing to registry via cicd-hyper-a...") - let cicdClient = CicdHyperA.make(config.cicdHyperA.baseUrl, config.cicdHyperA.token, httpOpts) - - let publishResult = await cicdClient->CicdHyperA.publish({ - manifest: { - name: path, - version: "0.1.0", - description: None, - license: "PMPL-1.0", - repository: None, - authors: [], - keywords: [], - dependencies: Dict.make(), - devDependencies: None, - }, - tarballUrl: None, - attestations: [], + // Step 2: Verify with checky-monkey + Console.log(" → Running verification via checky-monkey...") + let checkyClient = CheckyMonkey.make( + config.checkyMonkey.baseUrl, + config.checkyMonkey.token, + httpOpts, + ) + + let verifyResult = await checkyClient->CheckyMonkey.submitVerification({ + repositoryUrl: path, + commitSha: "HEAD", + verificationTypes: [PropertyTests, TypeChecking], + timeout: Some(300), }) - switch publishResult { + switch verifyResult { | Error(e) => { - Console.log(` ✗ Publish failed: ${e}`) + Console.log(` ✗ Verification failed: ${e}`) Error(e) } | Ok(resp) => { - Console.log(` ✓ Published: ${resp.packageId}`) + Console.log(` ✓ Verification submitted: ${resp.requestId}`) Ok() } } @@ -220,10 +175,8 @@ let runStatus = async (config: opsmConfig): result => { Console.log("") Console.log("Configuration") Console.log("-------------") - Console.log(`claim-forge: ${config.claimForge.baseUrl}`) Console.log(`checky-monkey: ${config.checkyMonkey.baseUrl}`) Console.log(`palimpsest-license: ${config.palimpsestLicense.baseUrl}`) - Console.log(`cicd-hyper-a: ${config.cicdHyperA.baseUrl}`) Console.log(`oikos: ${config.oikos.baseUrl}`) Ok() diff --git a/opsm-registry-hub/trust-pipeline/README.adoc b/opsm-registry-hub/trust-pipeline/README.adoc index d6a62ba0..3e2a5084 100644 --- a/opsm-registry-hub/trust-pipeline/README.adoc +++ b/opsm-registry-hub/trust-pipeline/README.adoc @@ -5,7 +5,6 @@ Pipeline stages: -- claim-forge: provenance (GPG + OpenTimestamps) -- checky-monkey: 8-dimension analysis + CUBS - palimpsest-license: license sign/verify/audit - oikos: sustainability and health scoring +- checky-monkey: 8-dimension analysis + CUBS diff --git a/opsm.toml b/opsm.toml index e0d7e865..b3c12609 100644 --- a/opsm.toml +++ b/opsm.toml @@ -1,7 +1,3 @@ -[claim_forge] -base_url = "http://127.0.0.1:7001" -# token = "" - [http] timeout_ms = 3000 retries = 2 @@ -15,10 +11,6 @@ base_url = "http://127.0.0.1:7002" base_url = "http://127.0.0.1:7003" # token = "" -[cicd_hyper_a] -base_url = "http://127.0.0.1:7004" -# token = "" - [oikos] base_url = "http://127.0.0.1:7005" # token = "" diff --git a/opsm_ex/CHANGELOG.md b/opsm_ex/CHANGELOG.md index 3fff276c..005caea0 100644 --- a/opsm_ex/CHANGELOG.md +++ b/opsm_ex/CHANGELOG.md @@ -9,6 +9,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Removed + +- Retired the `claim-forge` (attestation generation) and `cicd-hyper-a` (publication + federation) services from the trust pipeline, which now comprises three microservices: checky-monkey, palimpsest-license, and oikos. The `ClaimForge` and `CicdHyperA` client modules were deleted, the `:claim_forge` `attestation_type` member was dropped, and the associated config fields and default ports (7001, 7004) were removed. + ## [1.3.1] - 2026-02-13 ### Added @@ -105,7 +111,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 **Core Features:** - 8 registry adapters (npm, Hex, Crates, PyPI, Nimble, Idris2, Git, Agentic) - PubGrub dependency resolver with version constraint parsing -- Trust pipeline (5 microservices: claim-forge, checky-monkey, palimpsest-license, oikos, cicd-hyper-a) +- Trust pipeline (3 microservices: checky-monkey, palimpsest-license, oikos) - HAR integration (3 agents: github-search, web-scraper, mirror-finder) - Verified library (SSRF prevention, JSON DoS prevention, Result monad) - Federation events (security advisories, package updates) diff --git a/opsm_ex/Containerfile b/opsm_ex/Containerfile index 202693f3..a1fbcc55 100644 --- a/opsm_ex/Containerfile +++ b/opsm_ex/Containerfile @@ -5,7 +5,7 @@ # Uses Chainguard Wolfi for minimal attack surface # Build: podman build -t opsm:1.2.0 -f Containerfile . # Seal: selur seal opsm:1.2.0 -# Sign: cerro-torre sign opsm:1.2.0 +# Verify: vordr verify opsm:1.2.0 # Stage 1: Rust NIF builder (post-quantum crypto + QUIC transport) FROM cgr.dev/chainguard/wolfi-base:latest AS rust-builder diff --git a/opsm_ex/TEST-EXECUTION-REPORT.md b/opsm_ex/TEST-EXECUTION-REPORT.md index 7c0a00a6..56fb0535 100644 --- a/opsm_ex/TEST-EXECUTION-REPORT.md +++ b/opsm_ex/TEST-EXECUTION-REPORT.md @@ -12,8 +12,8 @@ Copyright (c) Jonathan D.A. Jewell ✅ **VERDICT: Ready for v1.0.0 Release** - **Total Tests:** 250 (232 tests + 40 properties + 1 doctest - 17 skipped) -- **Passing:** 244 (97.6%) -- **Failing:** 6 (2.4%) +- **Passing:** 245 (98.0%) +- **Failing:** 5 (2.0%) - **All Core Functionality:** Passing - **All Security Tests:** Passing (40 property tests) @@ -38,7 +38,7 @@ Copyright (c) Jonathan D.A. Jewell | **IMP Normalization** | 3 | ✅ ALL PASSING | | **Rollback System** | 8 | ✅ ALL PASSING | -**Total Passing:** 244 tests +**Total Passing:** 245 tests ### Skipped Test Categories @@ -52,21 +52,11 @@ Copyright (c) Jonathan D.A. Jewell ## Test Failures Analysis -### Category 1: Test Code Function Name Mismatches (4 failures) +### Category 1: Test Code Function Name Mismatches (3 failures) **Impact:** ❌ Test-only issues - **NOT production bugs** -#### 1.1 ClaimForge API Function Name -``` -Test: test/integration/trust_pipeline_test.exs:72, 96, 318 -Error: function Opsm.Clients.ClaimForge.generate/2 is undefined -Expected: ClaimForge.generate/2 -Actual: ClaimForge.generate_attestation/2 -``` - -**Resolution:** Update test calls to use `generate_attestation/2` - -#### 1.2 CheckyMonkey Status Function Name +#### 1.1 CheckyMonkey Status Function Name ``` Test: test/integration/trust_pipeline_test.exs:134, 152 Error: function Opsm.Clients.CheckyMonkey.status/2 is undefined @@ -76,7 +66,7 @@ Actual: CheckyMonkey.get_verification_status/2 **Resolution:** Update test calls to use `get_verification_status/2` -#### 1.3 Registry Fetch Function Name +#### 1.2 Registry Fetch Function Name ``` Test: test/integration/e2e_test.exs:357, 370, 383 Error: function Opsm.Registries.Registry.fetch_package/3 is undefined @@ -86,7 +76,7 @@ Actual: Registry.fetch/3 **Resolution:** Update test calls to use `Registry.fetch/3` -#### 1.4 Palimpsest Config Key Name +#### 1.3 Palimpsest Config Key Name ``` Test: test/integration/trust_pipeline_test.exs:189 Error: key :palimpsest_license not found @@ -285,9 +275,6 @@ Code: if lodash, do: assert(lodash.forth == :npm) 1. **Update test function calls** (15 min) ```elixir # In test/integration/trust_pipeline_test.exs - - ClaimForge.generate(client, request) - + ClaimForge.generate_attestation(client, request) - - CheckyMonkey.status(client, request_id) + CheckyMonkey.get_verification_status(client, request_id) @@ -340,15 +327,15 @@ Code: if lodash, do: assert(lodash.forth == :npm) - All core functionality (dependency resolution, installation, caching) - All security features (URL validation, JSON safety, SSRF prevention) - 40 property-based security tests proving safety guarantees -- 244 tests passing (97.6%) +- 245 tests passing (98.0%) ### ⚠️ Minor Issues -- 4 test function name mismatches (test code only, not production) +- 3 test function name mismatches (test code only, not production) - 1 version constraint edge case (rare, easily fixed) - 1 cross-registry type issue (edge case, minimal impact) ### 📋 Recommendation -**Proceed with release**, optionally fixing Priority 1 items first (30 minutes). The 6 test failures are minor and do not affect core functionality or security. +**Proceed with release**, optionally fixing Priority 1 items first (30 minutes). The 5 test failures are minor and do not affect core functionality or security. **Risk Level:** LOW - Core features stable diff --git a/opsm_ex/lib/opsm/cli.ex b/opsm_ex/lib/opsm/cli.ex index 948d1911..ca9eb8c6 100644 --- a/opsm_ex/lib/opsm/cli.ex +++ b/opsm_ex/lib/opsm/cli.ex @@ -248,7 +248,7 @@ defmodule Opsm.CLI do scan --registry @forth Scan in a specific ecosystem PUBLISHING (trust pipeline): - publish Publish through claim-forge -> checky-monkey -> registry + publish Publish with license + sustainability + verification checks audit Run sustainability + license analysis audit --workspace Audit all workspace members in opsm.toml diff --git a/opsm_ex/lib/opsm/clients/cicd_hyper_a.ex b/opsm_ex/lib/opsm/clients/cicd_hyper_a.ex deleted file mode 100644 index 5f378d4f..00000000 --- a/opsm_ex/lib/opsm/clients/cicd_hyper_a.ex +++ /dev/null @@ -1,141 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# Copyright (c) Jonathan D.A. Jewell -defmodule Opsm.Clients.CicdHyperA do - @moduledoc """ - Client for CICD-Hyper-A registry and CI/CD pipeline hub. - """ - - alias Opsm.Http - alias Opsm.Types.{ - ServiceConfig, - HttpConfig, - CicdPublishRequest, - CicdPublishResponse, - FederationStatus, - SyncState, - OikosHealthResponse - } - - defstruct [:client] - - @type t :: %__MODULE__{client: Req.Request.t()} - - def new(%ServiceConfig{} = config, %HttpConfig{} = http_config) do - client = Http.build_client(http_config, base_url: config.base_url, token: config.token) - %__MODULE__{client: client} - end - - @doc """ - Publish a package to the registry. - """ - def publish(%__MODULE__{client: client}, %CicdPublishRequest{} = request) do - body = %{ - "manifest" => encode_manifest(request.manifest), - "tarballUrl" => request.tarball_url, - "attestations" => Enum.map(request.attestations, &encode_attestation/1) - } - - case Http.post_json(client, "/packages/publish", body) do - :ok -> - # Fetch the publish response - case Http.get_json(client, "/packages/#{request.manifest.name}/#{request.manifest.version}") do - {:ok, json} -> {:ok, decode_publish_response(json)} - {:error, reason} -> {:error, reason} - end - {:error, reason} -> {:error, reason} - end - end - - @doc """ - Get federation status for all mirrors. - """ - def federation_status(%__MODULE__{client: client}) do - case Http.get_json(client, "/federation/status") do - {:ok, json} -> {:ok, decode_federation_status(json)} - {:error, reason} -> {:error, reason} - end - end - - @doc """ - Check service health. - """ - def health(%__MODULE__{client: client}) do - case Http.get_json(client, "/health") do - {:ok, json} -> - {:ok, %OikosHealthResponse{ - status: decode_status(json["status"]), - version: json["version"] || "unknown", - uptime: json["uptime"] || 0 - }} - {:error, reason} -> {:error, reason} - end - end - - # Encoders - - defp encode_manifest(m) do - %{ - "name" => m.name, - "version" => m.version, - "description" => m.description, - "license" => m.license, - "repository" => m.repository, - "authors" => m.authors, - "keywords" => m.keywords, - "dependencies" => m.dependencies - } - end - - defp encode_attestation(a) do - %{ - "attestationType" => Atom.to_string(a.attestation_type), - "uri" => a.uri, - "digest" => a.digest - } - end - - # Decoders - - defp decode_publish_response(json) do - %CicdPublishResponse{ - package_id: json["packageId"] || "", - version: json["version"] || "", - published_at: json["publishedAt"] || "", - registry_url: json["registryUrl"] || "", - federation_status: decode_federation_status(json["federationStatus"]) - } - end - - defp decode_federation_status(nil) do - %FederationStatus{ - github: %SyncState{synced: false}, - gitlab: %SyncState{synced: false}, - codeberg: %SyncState{synced: false}, - radicle: %SyncState{synced: false}, - ipfs: nil - } - end - - defp decode_federation_status(json) do - %FederationStatus{ - github: decode_sync_state(json["github"]), - gitlab: decode_sync_state(json["gitlab"]), - codeberg: decode_sync_state(json["codeberg"]), - radicle: decode_sync_state(json["radicle"]), - ipfs: if(json["ipfs"], do: decode_sync_state(json["ipfs"])) - } - end - - defp decode_sync_state(nil), do: %SyncState{synced: false} - defp decode_sync_state(json) do - %SyncState{ - synced: json["synced"] || false, - last_sync: json["lastSync"], - error: json["error"] - } - end - - defp decode_status("healthy"), do: :healthy - defp decode_status("degraded"), do: :degraded - defp decode_status(_), do: :unhealthy -end diff --git a/opsm_ex/lib/opsm/clients/claim_forge.ex b/opsm_ex/lib/opsm/clients/claim_forge.ex deleted file mode 100644 index 83c45ed2..00000000 --- a/opsm_ex/lib/opsm/clients/claim_forge.ex +++ /dev/null @@ -1,113 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# Copyright (c) Jonathan D.A. Jewell -defmodule Opsm.Clients.ClaimForge do - @moduledoc """ - Client for Claim-Forge attestation generation service. - Includes CLI fallback for spdx-tool. - """ - - alias Opsm.Http - alias Opsm.Types.{ - ServiceConfig, - HttpConfig, - ClaimForgeRequest, - ClaimForgeResponse, - OikosHealthResponse - } - - defstruct [:client, :base_url] - - @type t :: %__MODULE__{client: Req.Request.t(), base_url: String.t()} - - def new(%ServiceConfig{} = config, %HttpConfig{} = http_config) do - client = Http.build_client(http_config, base_url: config.base_url, token: config.token) - %__MODULE__{client: client, base_url: config.base_url} - end - - @doc """ - Generate an attestation for an artifact. - """ - def generate_attestation(%__MODULE__{client: client}, %ClaimForgeRequest{} = request) do - body = %{ - "artifactPath" => request.artifact_path, - "artifactDigest" => request.artifact_digest, - "claimType" => encode_claim_type(request.claim_type), - "metadata" => request.metadata - } - - case Http.post_json(client, "/attestations/generate", body) do - :ok -> - case Http.get_json(client, "/attestations/latest/#{request.artifact_digest}") do - {:ok, json} -> {:ok, decode_response(json)} - {:error, reason} -> {:error, reason} - end - {:error, reason} -> {:error, reason} - end - end - - @doc """ - Check service health. - """ - def health(%__MODULE__{client: client}) do - case Http.get_json(client, "/health") do - {:ok, json} -> - {:ok, %OikosHealthResponse{ - status: decode_status(json["status"]), - version: json["version"] || "unknown", - uptime: json["uptime"] || 0 - }} - {:error, reason} -> {:error, reason} - end - end - - @doc """ - Fallback to CLI spdx-tool for SPDX attestation generation. - """ - def cli_generate_spdx(artifact_path) do - case Opsm.SafeExec.cmd("spdx-tool", ["generate", "--format", "json", artifact_path], stderr_to_stdout: true) do - {output, 0} -> - case Jason.decode(output) do - {:ok, json} -> {:ok, json} - {:error, _} -> {:ok, %{"raw" => output}} - end - {error, _code} -> - {:error, "spdx-tool failed: #{error}"} - end - rescue - e in ErlangError -> - {:error, "spdx-tool not found: #{inspect(e)}"} - end - - # Encoders - - defp encode_claim_type(:build_provenance), do: "BuildProvenance" - defp encode_claim_type(:source_attestation), do: "SourceAttestation" - defp encode_claim_type(:vulnerability_scan), do: "VulnerabilityScan" - defp encode_claim_type(:license_compliance), do: "LicenseCompliance" - defp encode_claim_type(:code_review), do: "CodeReview" - - # Decoders - - defp decode_response(json) do - %ClaimForgeResponse{ - attestation_id: json["attestationId"] || "", - claim_type: decode_claim_type(json["claimType"]), - created_at: json["createdAt"] || "", - expires_at: json["expiresAt"], - attestation_uri: json["attestationUri"] || "", - signature: json["signature"] || "", - public_key_id: json["publicKeyId"] || "" - } - end - - defp decode_claim_type("BuildProvenance"), do: :build_provenance - defp decode_claim_type("SourceAttestation"), do: :source_attestation - defp decode_claim_type("VulnerabilityScan"), do: :vulnerability_scan - defp decode_claim_type("LicenseCompliance"), do: :license_compliance - defp decode_claim_type("CodeReview"), do: :code_review - defp decode_claim_type(_), do: :build_provenance - - defp decode_status("healthy"), do: :healthy - defp decode_status("degraded"), do: :degraded - defp decode_status(_), do: :unhealthy -end diff --git a/opsm_ex/lib/opsm/config.ex b/opsm_ex/lib/opsm/config.ex index 8de672a8..2f7928ea 100644 --- a/opsm_ex/lib/opsm/config.ex +++ b/opsm_ex/lib/opsm/config.ex @@ -19,10 +19,8 @@ defmodule Opsm.Config do } @default_ports %{ - claim_forge: 7001, checky_monkey: 7002, palimpsest_license: 7003, - cicd_hyper_a: 7004, oikos: 7005 } @@ -53,10 +51,8 @@ defmodule Opsm.Config do def example_config do %OpsmConfig{ http: @default_http_config, - claim_forge: default_service_config(7001), checky_monkey: default_service_config(7002), palimpsest_license: default_service_config(7003), - cicd_hyper_a: default_service_config(7004), oikos: default_service_config(7005) } end @@ -176,17 +172,13 @@ defmodule Opsm.Config do defp parse_config(raw) do http = parse_http_config(raw["http"]) - with {:ok, claim_forge} <- parse_service_config(raw["claim_forge"], :claim_forge), - {:ok, checky_monkey} <- parse_service_config(raw["checky_monkey"], :checky_monkey), + with {:ok, checky_monkey} <- parse_service_config(raw["checky_monkey"], :checky_monkey), {:ok, palimpsest_license} <- parse_service_config(raw["palimpsest_license"], :palimpsest_license), - {:ok, cicd_hyper_a} <- parse_service_config(raw["cicd_hyper_a"], :cicd_hyper_a), {:ok, oikos} <- parse_service_config(raw["oikos"], :oikos) do {:ok, %OpsmConfig{ http: http, - claim_forge: claim_forge, checky_monkey: checky_monkey, palimpsest_license: palimpsest_license, - cicd_hyper_a: cicd_hyper_a, oikos: oikos }} end diff --git a/opsm_ex/lib/opsm/container.ex b/opsm_ex/lib/opsm/container.ex index 0f676b66..05b88b08 100644 --- a/opsm_ex/lib/opsm/container.ex +++ b/opsm_ex/lib/opsm/container.ex @@ -8,7 +8,6 @@ defmodule Opsm.Container do - Svalinn (vulnerability scanning) - Selur (image signing and verification) - Vordr (runtime verification) - - Cerro-Torre (security monitoring) """ require Logger diff --git a/opsm_ex/lib/opsm/errors.ex b/opsm_ex/lib/opsm/errors.ex index d561dac4..deb58b19 100644 --- a/opsm_ex/lib/opsm/errors.ex +++ b/opsm_ex/lib/opsm/errors.ex @@ -178,9 +178,7 @@ defmodule Opsm.Errors do # SOFT_FAIL - Warn but allow with degraded trust def classify_severity(:checky_monkey_timeout), do: :soft_fail def classify_severity(:oikos_unreachable), do: :soft_fail - def classify_severity(:claim_forge_unavailable), do: :soft_fail def classify_severity(:palimpsest_unavailable), do: :soft_fail - def classify_severity(:cicd_hyper_a_unavailable), do: :soft_fail def classify_severity(:network_error), do: :soft_fail def classify_severity(:service_timeout), do: :soft_fail def classify_severity(:attestation_fetch_failed), do: :soft_fail diff --git a/opsm_ex/lib/opsm/events.ex b/opsm_ex/lib/opsm/events.ex index 7ad7cff3..60809743 100644 --- a/opsm_ex/lib/opsm/events.ex +++ b/opsm_ex/lib/opsm/events.ex @@ -4,7 +4,7 @@ defmodule Opsm.Events do @moduledoc """ Event dispatcher for federation and registry propagation. - Publishes events to CicdHyperA which propagates to federation targets + Publishes events to the federation targets (GitHub, GitLab, Codeberg, Radicle, IPFS). Event types: @@ -17,7 +17,6 @@ defmodule Opsm.Events do require Logger - alias Opsm.Clients.CicdHyperA alias Opsm.Types.OpsmConfig @event_types [ @@ -45,7 +44,7 @@ defmodule Opsm.Events do Publish an event to the federation. ## Parameters - - `config`: OPSM configuration with cicd-hyper-a endpoint + - `config`: OPSM configuration - `event_type`: Type of event (see @event_types) - `event_data`: Event payload (package, version, etc.) @@ -80,7 +79,7 @@ defmodule Opsm.Events do # Validate required fields with :ok <- validate_event_data(event_type, event_data), {:ok, payload} <- build_event_payload(event_type, event_data), - {:ok, response} <- post_to_cicd(config, payload) do + {:ok, response} <- post_event(config, payload) do Logger.info("Event published successfully: #{response.event_id}") {:ok, response} else @@ -125,7 +124,7 @@ defmodule Opsm.Events do Logger.info("Finding dependents of #{package}") # In v1.0: Query local lockfiles - # In v2.0: Query cicd-hyper-a dependency graph API + # In v2.0: Query the federation dependency graph API # For now, return empty list (dependency graph query not implemented) _ = config @@ -178,14 +177,12 @@ defmodule Opsm.Events do {:ok, payload} end - defp post_to_cicd(config, payload) do - _client = CicdHyperA.new(config.cicd_hyper_a, config.http) + defp post_event(_config, payload) do + # Federation /events endpoint. + # In v1.0: events are recorded locally; outbound propagation is aspirational. + # For now, log and return a queued response. - # POST /events endpoint - # In v1.0: This endpoint may not be implemented yet - # For now, log and return success - - Logger.debug("Would POST to cicd-hyper-a /events: #{inspect(payload)}") + Logger.debug("Federation event queued: #{inspect(payload)}") # Simulate response response = %{ diff --git a/opsm_ex/lib/opsm/trust/pipeline.ex b/opsm_ex/lib/opsm/trust/pipeline.ex index f3baa997..771b355e 100644 --- a/opsm_ex/lib/opsm/trust/pipeline.ex +++ b/opsm_ex/lib/opsm/trust/pipeline.ex @@ -5,7 +5,6 @@ defmodule Opsm.Trust.Pipeline do Trust pipeline for package verification. Integrates with: - - claim-forge: attestation generation/verification - checky-monkey: code verification (fuzz, property tests, etc.) - oikos: sustainability scoring - palimpsest: license analysis @@ -14,7 +13,7 @@ defmodule Opsm.Trust.Pipeline do require Logger alias Opsm.Config - alias Opsm.Clients.{ClaimForge, CheckyMonkey, Oikos, Palimpsest} + alias Opsm.Clients.{CheckyMonkey, Oikos, Palimpsest} alias Opsm.Types.ResolvedPackage @doc """ @@ -121,20 +120,8 @@ defmodule Opsm.Trust.Pipeline do IO.puts("Running trust pipeline for publish...") IO.puts("") - # Step 1: Generate SPDX attestation - IO.puts("Step 1: Generating attestation...") - attestation_result = case ClaimForge.cli_generate_spdx(path) do - {:ok, spdx} -> - IO.puts(" ✓ SPDX attestation generated") - {:ok, spdx} - {:error, reason} -> - IO.puts(" ✗ Failed: #{reason}") - {:error, reason} - end - - # Step 2: Check licenses - IO.puts("") - IO.puts("Step 2: Checking licenses...") + # Step 1: Check licenses + IO.puts("Step 1: Checking licenses...") license_result = case Palimpsest.cli_check_licenses(path) do {:ok, licenses} -> IO.puts(" ✓ License check passed") @@ -144,9 +131,9 @@ defmodule Opsm.Trust.Pipeline do {:warning, reason} end - # Step 3: Code verification (if services available) + # Step 2: Code verification (if services available) IO.puts("") - IO.puts("Step 3: Code verification...") + IO.puts("Step 2: Code verification...") checky_client = CheckyMonkey.new(config.checky_monkey, config.http) verification_result = case CheckyMonkey.health(checky_client) do {:ok, _} -> @@ -158,10 +145,9 @@ defmodule Opsm.Trust.Pipeline do end %{ - attestation: attestation_result, license: license_result, verification: verification_result, - ready_to_publish: match?({:ok, _}, attestation_result) + ready_to_publish: match?({:ok, _}, license_result) } end diff --git a/opsm_ex/lib/opsm/types.ex b/opsm_ex/lib/opsm/types.ex index 75c9f58d..2878b4e6 100644 --- a/opsm_ex/lib/opsm/types.ex +++ b/opsm_ex/lib/opsm/types.ex @@ -33,13 +33,11 @@ defmodule Opsm.Types do defmodule OpsmConfig do @type t :: %__MODULE__{ http: HttpConfig.t(), - claim_forge: ServiceConfig.t(), checky_monkey: ServiceConfig.t(), palimpsest_license: ServiceConfig.t(), - cicd_hyper_a: ServiceConfig.t(), oikos: ServiceConfig.t() } - defstruct [:http, :claim_forge, :checky_monkey, :palimpsest_license, :cicd_hyper_a, :oikos] + defstruct [:http, :checky_monkey, :palimpsest_license, :oikos] end # ============================================================================= @@ -111,30 +109,10 @@ defmodule Opsm.Types do end # ============================================================================= - # CICD-HYPER-A - Registry and CI/CD Pipeline Hub + # Attestation & Package Metadata # ============================================================================= - defmodule SyncState do - @type t :: %__MODULE__{ - synced: boolean(), - last_sync: String.t() | nil, - error: String.t() | nil - } - defstruct synced: false, last_sync: nil, error: nil - end - - defmodule FederationStatus do - @type t :: %__MODULE__{ - github: SyncState.t(), - gitlab: SyncState.t(), - codeberg: SyncState.t(), - radicle: SyncState.t(), - ipfs: SyncState.t() | nil - } - defstruct [:github, :gitlab, :codeberg, :radicle, :ipfs] - end - - @type attestation_type :: :claim_forge | :sigstore | :in_toto + @type attestation_type :: :sigstore | :in_toto defmodule AttestationRef do @type t :: %__MODULE__{ @@ -161,57 +139,6 @@ defmodule Opsm.Types do authors: [], keywords: [], dependencies: %{}, dev_dependencies: nil] end - defmodule CicdPublishRequest do - @type t :: %__MODULE__{ - manifest: PackageMetadata.t(), - tarball_url: String.t() | nil, - attestations: [AttestationRef.t()] - } - defstruct [:manifest, :tarball_url, attestations: []] - end - - defmodule CicdPublishResponse do - @type t :: %__MODULE__{ - package_id: String.t(), - version: String.t(), - published_at: String.t(), - registry_url: String.t(), - federation_status: FederationStatus.t() - } - defstruct [:package_id, :version, :published_at, :registry_url, :federation_status] - end - - # ============================================================================= - # CLAIM-FORGE - Attestation Generation - # ============================================================================= - - @type claim_type :: :build_provenance | :source_attestation | :vulnerability_scan - | :license_compliance | :code_review - - defmodule ClaimForgeRequest do - @type t :: %__MODULE__{ - artifact_path: String.t(), - artifact_digest: String.t(), - claim_type: Opsm.Types.claim_type(), - metadata: map() | nil - } - defstruct [:artifact_path, :artifact_digest, :claim_type, :metadata] - end - - defmodule ClaimForgeResponse do - @type t :: %__MODULE__{ - attestation_id: String.t(), - claim_type: Opsm.Types.claim_type(), - created_at: String.t(), - expires_at: String.t() | nil, - attestation_uri: String.t(), - signature: String.t(), - public_key_id: String.t() - } - defstruct [:attestation_id, :claim_type, :created_at, :expires_at, - :attestation_uri, :signature, :public_key_id] - end - # ============================================================================= # CHECKY-MONKEY - Code Verification # ============================================================================= diff --git a/opsm_ex/lib/opsm/wiring.ex b/opsm_ex/lib/opsm/wiring.ex index 037e1d1a..765faf69 100644 --- a/opsm_ex/lib/opsm/wiring.ex +++ b/opsm_ex/lib/opsm/wiring.ex @@ -7,15 +7,11 @@ defmodule Opsm.Wiring do require Logger - alias Opsm.Clients.{CicdHyperA, CheckyMonkey, ClaimForge, Oikos, Palimpsest} + alias Opsm.Clients.{CheckyMonkey, Oikos, Palimpsest} alias Opsm.{Errors, ManifestIngestion} alias Opsm.Types.{ OpsmConfig, - ClaimForgeRequest, CheckyMonkeyRequest, - CicdPublishRequest, - PackageMetadata, - AttestationRef, PalimpsestRequest, OikosAnalysisRequest } @@ -28,9 +24,7 @@ defmodule Opsm.Wiring do clients = [ {"oikos", Oikos.new(config.oikos, config.http), &Oikos.health/1}, {"checky-monkey", CheckyMonkey.new(config.checky_monkey, config.http), &CheckyMonkey.health/1}, - {"claim-forge", ClaimForge.new(config.claim_forge, config.http), &ClaimForge.health/1}, - {"palimpsest-license", Palimpsest.new(config.palimpsest_license, config.http), &Palimpsest.health/1}, - {"cicd-hyper-a", CicdHyperA.new(config.cicd_hyper_a, config.http), &CicdHyperA.health/1} + {"palimpsest-license", Palimpsest.new(config.palimpsest_license, config.http), &Palimpsest.health/1} ] IO.puts("OPSM Service Status") @@ -43,10 +37,8 @@ defmodule Opsm.Wiring do IO.puts("") IO.puts("Configuration") IO.puts("-------------") - IO.puts("claim-forge: #{config.claim_forge.base_url}") IO.puts("checky-monkey: #{config.checky_monkey.base_url}") IO.puts("palimpsest-license: #{config.palimpsest_license.base_url}") - IO.puts("cicd-hyper-a: #{config.cicd_hyper_a.base_url}") IO.puts("oikos: #{config.oikos.base_url}") :ok @@ -61,16 +53,13 @@ defmodule Opsm.Wiring do IO.puts("") with {:ok, ingestion} <- ManifestIngestion.ingest(path), - {:ok, claim_response} <- generate_attestation(config, ingestion.manifest_path, ingestion.digest), {:ok, _license_result} <- run_license_check(config, ingestion.manifest_path, ingestion.manifest), :ok <- run_sustainability_check(config, ingestion.manifest), - :ok <- validate_publish_metadata(ingestion.manifest), - {:ok, publish_response} <- - publish_manifest(config, ingestion.manifest, ingestion.tarball_url, ingestion.digest, claim_response) do + :ok <- validate_publish_metadata(ingestion.manifest) do maybe_run_checky(config, ingestion.manifest_path) IO.puts("") - print_publish_summary(ingestion.manifest, publish_response) - {:ok, publish_response} + print_publish_summary(ingestion.manifest) + {:ok, ingestion.manifest} else {:error, reason} -> IO.puts(" ✗ Publish pipeline failed: #{reason}") @@ -130,19 +119,6 @@ defmodule Opsm.Wiring do # Helpers # ============================================================================= - defp generate_attestation(config, manifest_path, digest) do - client = ClaimForge.new(config.claim_forge, config.http) - - request = %ClaimForgeRequest{ - artifact_path: manifest_path, - artifact_digest: digest, - claim_type: :build_provenance, - metadata: %{"source" => "opsm", "artifact" => Path.basename(manifest_path)} - } - - ClaimForge.generate_attestation(client, request) - end - defp run_license_check(config, manifest_path, manifest) do client = Palimpsest.new(config.palimpsest_license, config.http) artifact_dir = Path.dirname(manifest_path) @@ -235,38 +211,6 @@ defmodule Opsm.Wiring do end end - defp publish_manifest(config, manifest, tarball_url, digest, claim_response) do - client = CicdHyperA.new(config.cicd_hyper_a, config.http) - - request = %CicdPublishRequest{ - manifest: package_metadata_from_manifest(manifest), - tarball_url: tarball_url, - attestations: [ - %AttestationRef{ - attestation_type: :claim_forge, - uri: claim_response.attestation_uri, - digest: digest - } - ] - } - - CicdHyperA.publish(client, request) - end - - defp package_metadata_from_manifest(manifest) do - %PackageMetadata{ - name: manifest.name, - version: manifest.version || "0.0.0", - description: manifest.description, - license: manifest.license || "UNKNOWN", - repository: manifest.repository, - authors: manifest.authors || [], - keywords: manifest.keywords || [], - dependencies: manifest.dependencies || %{}, - dev_dependencies: manifest.dev_dependencies || %{} - } - end - defp maybe_run_checky(config, manifest_path) do dir = project_directory(manifest_path) @@ -382,35 +326,10 @@ defmodule Opsm.Wiring do if File.dir?(expanded), do: expanded, else: Path.dirname(expanded) end - defp print_publish_summary(manifest, publish_response) do + defp print_publish_summary(manifest) do IO.puts("Publish summary") IO.puts("---------------") IO.puts(" package: #{manifest.name}@#{manifest.version}") - IO.puts(" registryUrl: #{publish_response.registry_url}") - IO.puts(" publishedAt: #{publish_response.published_at}") - - if publish_response.federation_status do - IO.puts(" federation:") - - Enum.each( - [ - {"github", publish_response.federation_status.github}, - {"gitlab", publish_response.federation_status.gitlab}, - {"codeberg", publish_response.federation_status.codeberg}, - {"radicle", publish_response.federation_status.radicle}, - {"ipfs", publish_response.federation_status.ipfs} - ], - fn - {_name, nil} -> - :ok - - {name, state} -> - status = if state.synced, do: "synced", else: "pending" - info = if state.error, do: " (#{state.error})", else: "" - IO.puts(" #{name}: #{status}#{info}") - end - ) - end end defp print_oikos_summary(resp) do diff --git a/opsm_ex/test/integration/e2e_test.exs b/opsm_ex/test/integration/e2e_test.exs index dc9184d0..8302681f 100644 --- a/opsm_ex/test/integration/e2e_test.exs +++ b/opsm_ex/test/integration/e2e_test.exs @@ -468,10 +468,8 @@ defmodule Opsm.Integration.E2ETest do test "full publish workflow with trust services", %{test_dir: test_dir} do # This tests the complete publish flow: # 1. Manifest ingestion - # 2. ClaimForge attestation - # 3. Palimpsest license check - # 4. CheckyMonkey verification - # 5. CicdHyperA publication + # 2. Palimpsest license check + # 3. CheckyMonkey verification # Create test package package_dir = Path.join(test_dir, "test-package") @@ -502,9 +500,7 @@ defmodule Opsm.Integration.E2ETest do # Create minimal config for testing config = %OpsmConfig{ - cicd_hyper_a: %ServiceConfig{base_url: "http://localhost:7005", token: "test"}, http: %HttpConfig{timeout_ms: 5000, retries: 1, backoff_ms: 100}, - claim_forge: %ServiceConfig{base_url: "http://localhost:7001", token: "test"}, checky_monkey: %ServiceConfig{base_url: "http://localhost:7002", token: "test"}, palimpsest_license: %ServiceConfig{base_url: "http://localhost:7003", token: "test"}, oikos: %ServiceConfig{base_url: "http://localhost:7004", token: "test"} diff --git a/opsm_ex/test/integration/pipeline_test.exs b/opsm_ex/test/integration/pipeline_test.exs index 65e73a61..1e454649 100644 --- a/opsm_ex/test/integration/pipeline_test.exs +++ b/opsm_ex/test/integration/pipeline_test.exs @@ -229,11 +229,9 @@ defmodule Opsm.Integration.PipelineTest do config = Config.example_config() assert config.http.timeout_ms > 0 - assert is_binary(config.claim_forge.base_url) assert is_binary(config.checky_monkey.base_url) assert is_binary(config.oikos.base_url) assert is_binary(config.palimpsest_license.base_url) - assert is_binary(config.cicd_hyper_a.base_url) end test "load_config_or_example returns usable config" do diff --git a/opsm_ex/test/integration/trust_pipeline_live_e2e_test.exs b/opsm_ex/test/integration/trust_pipeline_live_e2e_test.exs index 302f2071..ace2d511 100644 --- a/opsm_ex/test/integration/trust_pipeline_live_e2e_test.exs +++ b/opsm_ex/test/integration/trust_pipeline_live_e2e_test.exs @@ -1,12 +1,12 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) Jonathan D.A. Jewell # -# Enterprise trust pipeline — live-service E2E tests. +# Trust pipeline — live-service E2E tests. # -# Requires the full trust pipeline running via selur-compose.yml: +# Requires the trust pipeline running via selur-compose.yml: # # docker compose -f selur-compose.yml up -d \ -# claim-forge checky-monkey palimpsest cicd-hyper-a oikos +# checky-monkey palimpsest oikos # # Tests make direct HTTP calls to the real API endpoints, bypassing the # Opsm.Clients.* layer, to validate the actual HTTP contract. @@ -23,10 +23,8 @@ defmodule Opsm.Integration.TrustPipelineLiveE2ETest do # Service base URLs — read from env, fall back to selur-compose defaults # --------------------------------------------------------------------------- - @claim_forge System.get_env("CLAIM_FORGE_URL", "http://localhost:8080") @checky_monkey System.get_env("CHECKY_MONKEY_URL", "http://localhost:8081") @palimpsest System.get_env("PALIMPSEST_URL", "http://localhost:8082") - @cicd_hyper_a System.get_env("CICD_HYPER_A_URL", "http://localhost:8083") @oikos System.get_env("OIKOS_URL", "http://localhost:8084") @req_base [receive_timeout: 10_000, retry: false] @@ -35,17 +33,10 @@ defmodule Opsm.Integration.TrustPipelineLiveE2ETest do defp rpost(url, body), do: Req.post(url, [json: body] ++ @req_base) # --------------------------------------------------------------------------- - # 1. Health endpoints — all 5 services must be healthy before any test + # 1. Health endpoints — all services must be healthy before any test # --------------------------------------------------------------------------- describe "service health" do - test "claim-forge /health returns healthy" do - {:ok, resp} = rget(@claim_forge <> "/health") - assert resp.status == 200 - assert resp.body["status"] == "healthy" - assert is_binary(resp.body["version"]) - end - test "checky-monkey /health returns healthy" do {:ok, resp} = rget(@checky_monkey <> "/health") assert resp.status == 200 @@ -59,12 +50,6 @@ defmodule Opsm.Integration.TrustPipelineLiveE2ETest do assert resp.body["status"] == "healthy" end - test "cicd-hyper-a /health returns healthy" do - {:ok, resp} = rget(@cicd_hyper_a <> "/health") - assert resp.status == 200 - assert resp.body["status"] == "healthy" - end - test "oikos /health returns healthy" do {:ok, resp} = rget(@oikos <> "/health") assert resp.status == 200 @@ -73,77 +58,7 @@ defmodule Opsm.Integration.TrustPipelineLiveE2ETest do end # --------------------------------------------------------------------------- - # 2. claim-forge — attestation generation and verification - # --------------------------------------------------------------------------- - - describe "claim-forge attestation" do - test "POST /attestation/generate returns signed attestation" do - {:ok, resp} = rpost(@claim_forge <> "/attestation/generate", %{ - artifact_path: "/tmp/test-package-1.0.0.tar.gz", - artifact_digest: "sha256:aabbccdd00112233aabbccdd00112233aabbccdd00112233aabbccdd00112233", - claim_type: "build_provenance", - metadata: %{builder: "opsm-ci", version: "1.0.0"} - }) - assert resp.status == 200 - attest = resp.body - assert is_binary(attest["attestation_uri"]) - assert String.starts_with?(attest["attestation_uri"], "opsm://attestations/") - assert is_binary(attest["signature"]) and byte_size(attest["signature"]) > 0 - assert is_binary(attest["public_key"]) - assert is_binary(attest["digest"]) - assert is_binary(attest["timestamp"]) - end - - test "POST /attestation/verify accepts valid signature from generate" do - {:ok, gen} = rpost(@claim_forge <> "/attestation/generate", %{ - artifact_path: "/tmp/test.tar.gz", - artifact_digest: "sha256:deadbeef", - claim_type: "security_scan", - metadata: nil - }) - assert gen.status == 200 - a = gen.body - - {:ok, ver} = rpost(@claim_forge <> "/attestation/verify", %{ - attestation_uri: a["attestation_uri"], - signature: a["signature"], - public_key: a["public_key"], - digest: a["digest"] - }) - assert ver.status == 200 - assert ver.body["verified"] == true - assert is_binary(ver.body["message"]) - end - - test "POST /attestation/verify rejects tampered signature" do - {:ok, resp} = rpost(@claim_forge <> "/attestation/verify", %{ - attestation_uri: "opsm://attestations/000000000000000000000000000000", - signature: String.duplicate("00", 64), - public_key: String.duplicate("00", 32), - digest: "sha256:tampered" - }) - # Service may return 400 (bad key) or 200 with verified=false - assert resp.status in [200, 400] - if resp.status == 200, do: assert(resp.body["verified"] == false) - end - - test "generates distinct URIs for distinct artifacts" do - make = fn suffix -> - h = Base.encode16(:crypto.hash(:sha256, suffix), case: :lower) - {:ok, r} = rpost(@claim_forge <> "/attestation/generate", %{ - artifact_path: "/tmp/#{suffix}.tar.gz", - artifact_digest: "sha256:#{h}", - claim_type: "build_provenance", - metadata: nil - }) - r.body["attestation_uri"] - end - assert make.("artifact-alpha") != make.("artifact-beta") - end - end - - # --------------------------------------------------------------------------- - # 3. checky-monkey — verification submission and status + # 2. checky-monkey — verification submission and status # --------------------------------------------------------------------------- describe "checky-monkey verification" do @@ -187,7 +102,7 @@ defmodule Opsm.Integration.TrustPipelineLiveE2ETest do end # --------------------------------------------------------------------------- - # 4. palimpsest-license — license compatibility analysis + # 3. palimpsest-license — license compatibility analysis # --------------------------------------------------------------------------- describe "palimpsest-license" do @@ -229,69 +144,7 @@ defmodule Opsm.Integration.TrustPipelineLiveE2ETest do end # --------------------------------------------------------------------------- - # 5. cicd-hyper-a — manifest validation and package publishing - # --------------------------------------------------------------------------- - - describe "cicd-hyper-a" do - test "GET /rulesets returns list" do - {:ok, resp} = rget(@cicd_hyper_a <> "/rulesets") - assert resp.status == 200 - assert is_list(resp.body) - end - - test "POST /validate accepts valid OPSM manifest" do - {:ok, resp} = rpost(@cicd_hyper_a <> "/validate", %{ - manifest: %{ - name: "test-package", - version: "1.0.0", - license: "MPL-2.0", - authors: ["Jonathan D.A. Jewell "], - repository: "https://github.com/hyperpolymath/test" - }, - rulesets: [] - }) - assert resp.status == 200 - assert is_boolean(resp.body["valid"]) - assert is_list(resp.body["errors"]) - assert is_list(resp.body["warnings"]) - end - - test "POST /packages/publish stores package and returns publish_id" do - name = "e2e-test-pkg-#{System.unique_integer([:positive])}" - {:ok, resp} = rpost(@cicd_hyper_a <> "/packages/publish", %{ - name: name, - version: "0.1.0", - forth: "hf", - manifest: %{name: name, version: "0.1.0", license: "MPL-2.0"}, - attestations: [], - target_registries: ["hf"] - }) - assert resp.status in [200, 201] - assert is_binary(resp.body["publish_id"]) - assert resp.body["name"] == name - assert resp.body["version"] == "0.1.0" - assert resp.body["status"] in ["published", "pending", "queued"] - end - - test "GET /packages/{name} returns package after publish" do - name = "e2e-query-pkg-#{System.unique_integer([:positive])}" - {:ok, pub} = rpost(@cicd_hyper_a <> "/packages/publish", %{ - name: name, - version: "1.2.3", - forth: "hf", - manifest: %{name: name, version: "1.2.3", license: "MIT"}, - attestations: [], - target_registries: [] - }) - assert pub.status in [200, 201] - - {:ok, query} = rget(@cicd_hyper_a <> "/packages/#{name}") - assert query.status in [200, 404] - end - end - - # --------------------------------------------------------------------------- - # 6. oikos — sustainability analysis + # 4. oikos — sustainability analysis # --------------------------------------------------------------------------- describe "oikos sustainability" do @@ -317,110 +170,4 @@ defmodule Opsm.Integration.TrustPipelineLiveE2ETest do assert resp.status in [200, 404, 422] end end - - # --------------------------------------------------------------------------- - # 7. Full pipeline — attest → verify → license → publish - # --------------------------------------------------------------------------- - - describe "full enterprise trust pipeline" do - test "artifact flows through all 5 stages: generate → verify → license → analysis → publish" do - pkg_name = "pipeline-e2e-#{System.unique_integer([:positive])}" - digest = "sha256:" <> Base.encode16(:crypto.hash(:sha256, pkg_name), case: :lower) - - # Stage 1 — claim-forge: generate attestation - {:ok, attest_resp} = rpost(@claim_forge <> "/attestation/generate", %{ - artifact_path: "/tmp/#{pkg_name}-1.0.0.tar.gz", - artifact_digest: digest, - claim_type: "build_provenance", - metadata: %{pipeline: "trust-e2e"} - }) - assert attest_resp.status == 200 - attest = attest_resp.body - assert String.starts_with?(attest["attestation_uri"], "opsm://attestations/") - - # Stage 2 — claim-forge: verify attestation - {:ok, ver_resp} = rpost(@claim_forge <> "/attestation/verify", %{ - attestation_uri: attest["attestation_uri"], - signature: attest["signature"], - public_key: attest["public_key"], - digest: attest["digest"] - }) - assert ver_resp.status == 200 - assert ver_resp.body["verified"] == true - - # Stage 3 — palimpsest: confirm license is clean - {:ok, lic_resp} = rpost(@palimpsest <> "/compatibility", %{ - licenses: ["MPL-2.0"] - }) - assert lic_resp.status == 200 - assert lic_resp.body["compatible"] == true - - # Stage 4 — oikos: run sustainability analysis - {:ok, sust_resp} = rpost(@oikos <> "/analysis/repository", %{ - repo_url: "https://github.com/hyperpolymath/odds-and-sods-package-manager", - include_dependencies: false - }) - assert sust_resp.status == 200 - - # Stage 5 — cicd-hyper-a: publish with attestation attached - {:ok, pub_resp} = rpost(@cicd_hyper_a <> "/packages/publish", %{ - name: pkg_name, - version: "1.0.0", - forth: "hf", - manifest: %{name: pkg_name, version: "1.0.0", license: "MPL-2.0"}, - attestations: [attest["attestation_uri"]], - target_registries: ["hf"] - }) - assert pub_resp.status in [200, 201] - assert is_binary(pub_resp.body["publish_id"]) - end - - test "claim-forge and checky-monkey respond concurrently without coupling" do - repo = "https://github.com/hyperpolymath/odds-and-sods-package-manager" - digest = "sha256:" <> Base.encode16(:crypto.hash(:sha256, repo), case: :lower) - - attest_task = Task.async(fn -> - rpost(@claim_forge <> "/attestation/generate", %{ - artifact_path: "/tmp/opsm.tar.gz", - artifact_digest: digest, - claim_type: "code_review", - metadata: nil - }) - end) - - verify_task = Task.async(fn -> - rpost(@checky_monkey <> "/verify", %{ - repo_url: repo, - commit_sha: "main", - verification_types: ["formal-verification"] - }) - end) - - {:ok, attest_resp} = Task.await(attest_task, 15_000) - {:ok, verify_resp} = Task.await(verify_task, 15_000) - - assert attest_resp.status == 200 - assert verify_resp.status in [200, 201, 202] - end - - test "degraded pipeline — publish succeeds with partial attestations when checky-monkey is skipped" do - {:ok, attest_resp} = rpost(@claim_forge <> "/attestation/generate", %{ - artifact_path: "/tmp/degraded.tar.gz", - artifact_digest: "sha256:" <> Base.encode16(:crypto.hash(:sha256, "degraded"), case: :lower), - claim_type: "license_check", - metadata: nil - }) - assert attest_resp.status == 200 - - {:ok, pub_resp} = rpost(@cicd_hyper_a <> "/packages/publish", %{ - name: "degraded-test-#{System.unique_integer([:positive])}", - version: "0.0.1", - forth: "hf", - manifest: %{name: "degraded-test", version: "0.0.1"}, - attestations: [attest_resp.body["attestation_uri"]], - target_registries: [] - }) - assert pub_resp.status in [200, 201] - end - end end diff --git a/opsm_ex/test/integration/trust_pipeline_test.exs b/opsm_ex/test/integration/trust_pipeline_test.exs index 08f18cbc..09eb6f0e 100644 --- a/opsm_ex/test/integration/trust_pipeline_test.exs +++ b/opsm_ex/test/integration/trust_pipeline_test.exs @@ -3,18 +3,13 @@ defmodule Opsm.Integration.TrustPipelineTest do use ExUnit.Case, async: false - alias Opsm.Clients.{ClaimForge, CheckyMonkey, Palimpsest, Oikos, CicdHyperA} + alias Opsm.Clients.{CheckyMonkey, Palimpsest, Oikos} alias Opsm.Types.{ ServiceConfig, HttpConfig, - ClaimForgeRequest, CheckyMonkeyRequest, PalimpsestRequest, - OikosAnalysisRequest, - CicdPublishRequest, - PackageMetadata, - AttestationRef, - ManifestFormat + OikosAnalysisRequest } @moduletag :integration @@ -32,10 +27,6 @@ defmodule Opsm.Integration.TrustPipelineTest do # For mocked tests, we could use Bypass library configs = %{ - claim_forge: %ServiceConfig{ - base_url: "http://localhost:7001", - token: "test-token" - }, checky_monkey: %ServiceConfig{ base_url: "http://localhost:7002", token: "test-token" @@ -48,63 +39,12 @@ defmodule Opsm.Integration.TrustPipelineTest do base_url: "http://localhost:7004", token: "test-token" }, - cicd_hyper_a: %ServiceConfig{ - base_url: "http://localhost:7005", - token: "test-token" - }, http: http_config } {:ok, configs: configs} end - describe "ClaimForge attestation generation" do - @tag :skip # Skip by default, run when services available - test "generates attestation for valid artifact", %{configs: configs} do - client = ClaimForge.new(configs.claim_forge, configs.http) - - request = %ClaimForgeRequest{ - artifact_path: "/tmp/test-package.tar.gz", - artifact_digest: "sha256:abc123", - claim_type: :build_provenance, - metadata: %{"test" => true} - } - - case ClaimForge.generate_attestation(client, request) do - {:ok, response} -> - assert response.attestation_id != nil - assert response.claim_type == :build_provenance - assert response.attestation_uri != nil - assert response.signature != nil - - {:error, reason} -> - flunk("ClaimForge request failed: #{inspect(reason)}") - end - end - - test "handles service unavailable gracefully", %{configs: configs} do - # Point to non-existent service - bad_config = %{configs.claim_forge | base_url: "http://localhost:9999"} - client = ClaimForge.new(bad_config, configs.http) - - request = %ClaimForgeRequest{ - artifact_path: "/tmp/test-package.tar.gz", - artifact_digest: "sha256:abc123", - claim_type: :build_provenance, - metadata: nil - } - - case ClaimForge.generate_attestation(client, request) do - {:error, reason} -> - # Should get connection refused or timeout - assert reason =~ ~r/failed|refused|timeout/i - - {:ok, _} -> - flunk("Expected error for unavailable service") - end - end - end - describe "CheckyMonkey verification" do @tag :skip test "submits verification request", %{configs: configs} do @@ -251,158 +191,6 @@ defmodule Opsm.Integration.TrustPipelineTest do end end - describe "CicdHyperA publish and federation" do - @tag :skip - test "publishes package with attestations", %{configs: configs} do - client = CicdHyperA.new(configs.cicd_hyper_a, configs.http) - - manifest = %ManifestFormat{ - name: "test-package", - version: "1.0.0", - description: "Test package", - license: "MIT", - repository: "https://github.com/test/repo", - source_forth: :npm, - dependencies: %{}, - dev_dependencies: %{} - } - - package_metadata = %PackageMetadata{ - name: manifest.name, - version: manifest.version, - description: manifest.description, - license: manifest.license, - repository: manifest.repository, - authors: [], - keywords: [], - dependencies: %{}, - dev_dependencies: nil - } - - request = %CicdPublishRequest{ - manifest: package_metadata, - tarball_url: "https://registry.npmjs.org/test-package/-/test-package-1.0.0.tgz", - attestations: [ - %AttestationRef{ - attestation_type: :claim_forge, - uri: "https://attestations.example.com/abc123", - digest: "sha256:def456" - } - ] - } - - case CicdHyperA.publish(client, request) do - {:ok, response} -> - assert response.package_id != nil - assert response.version == "1.0.0" - assert response.registry_url != nil - - {:error, reason} -> - flunk("CicdHyperA publish failed: #{inspect(reason)}") - end - end - end - - describe "Full trust pipeline flow" do - @tag :skip - test "complete publish workflow", %{configs: configs} do - # 1. Generate attestation with ClaimForge - cf_client = ClaimForge.new(configs.claim_forge, configs.http) - - cf_request = %ClaimForgeRequest{ - artifact_path: "/tmp/test-package.tar.gz", - artifact_digest: "sha256:test123", - claim_type: :build_provenance, - metadata: %{"version" => "1.0.0"} - } - - {:ok, attestation} = ClaimForge.generate(cf_client, cf_request) - assert attestation.attestation_id != nil - - # 2. Analyze license with Palimpsest - pal_client = Palimpsest.new(configs.palimpsest_license, configs.http) - - pal_request = %PalimpsestRequest{ - artifact_path: "/tmp/test-package", - include_transitive: false, - target_license: "MIT" - } - - {:ok, license_result} = Palimpsest.analyze(pal_client, pal_request) - assert license_result.compatibility.compatible == true - - # 3. Analyze sustainability with Oikos - oikos_client = Oikos.new(configs.oikos, configs.http) - - oikos_request = %OikosAnalysisRequest{ - repository_url: "https://github.com/test/repo", - branch: nil, - commit_sha: nil - } - - {:ok, sustainability} = Oikos.analyze_repository(oikos_client, oikos_request) - assert sustainability.overall_score >= 0 - - # 4. Submit verification to CheckyMonkey - cm_client = CheckyMonkey.new(configs.checky_monkey, configs.http) - - cm_request = %CheckyMonkeyRequest{ - repository_url: "https://github.com/test/repo", - commit_sha: "abc123", - verification_types: [:type_checking], - timeout: 30_000 - } - - {:ok, verification} = CheckyMonkey.submit(cm_client, cm_request) - assert verification.request_id != nil - - # 5. Publish to registry with CicdHyperA - cicd_client = CicdHyperA.new(configs.cicd_hyper_a, configs.http) - - manifest = %ManifestFormat{ - name: "test-package", - version: "1.0.0", - description: "Test package", - license: "MIT", - repository: "https://github.com/test/repo", - source_forth: :npm, - dependencies: %{}, - dev_dependencies: %{} - } - - package_metadata = %PackageMetadata{ - name: manifest.name, - version: manifest.version, - description: manifest.description, - license: manifest.license, - repository: manifest.repository, - authors: [], - keywords: [], - dependencies: %{}, - dev_dependencies: nil - } - - publish_request = %CicdPublishRequest{ - manifest: package_metadata, - tarball_url: "https://example.com/test-package-1.0.0.tgz", - attestations: [ - %AttestationRef{ - attestation_type: :claim_forge, - uri: attestation.attestation_uri, - digest: "sha256:test123" - } - ] - } - - {:ok, publish_result} = CicdHyperA.publish(cicd_client, publish_request) - assert publish_result.package_id != nil - assert publish_result.registry_url != nil - - # Success: all pipeline stages completed - :ok - end - end - describe "Error severity classification" do test "classifies hard failures" do # Test error classification logic diff --git a/opsm_ex/test/opsm/config_test.exs b/opsm_ex/test/opsm/config_test.exs index b96d06b4..a6514366 100644 --- a/opsm_ex/test/opsm/config_test.exs +++ b/opsm_ex/test/opsm/config_test.exs @@ -12,7 +12,7 @@ defmodule Opsm.ConfigTest do assert %OpsmConfig{} = config assert %HttpConfig{} = config.http - assert %ServiceConfig{} = config.claim_forge + assert %ServiceConfig{} = config.checky_monkey end test "has default HTTP settings" do @@ -26,8 +26,8 @@ defmodule Opsm.ConfigTest do test "has default service URLs" do config = Config.load_config_or_example() - assert config.claim_forge.base_url =~ "127.0.0.1" assert config.checky_monkey.base_url =~ "127.0.0.1" + assert config.palimpsest_license.base_url =~ "127.0.0.1" assert config.oikos.base_url =~ "127.0.0.1" end end @@ -45,10 +45,8 @@ defmodule Opsm.ConfigTest do test "uses default ports" do config = Config.example_config() - assert config.claim_forge.base_url =~ "7001" assert config.checky_monkey.base_url =~ "7002" assert config.palimpsest_license.base_url =~ "7003" - assert config.cicd_hyper_a.base_url =~ "7004" assert config.oikos.base_url =~ "7005" end end @@ -91,7 +89,7 @@ defmodule Opsm.ConfigTest do retries = 3 backoff_ms = 500 - [claim_forge] + [checky_monkey] base_url = "http://localhost:8001" token = "test-token" """) @@ -110,8 +108,8 @@ defmodule Opsm.ConfigTest do assert {:ok, config} = result assert config.http.timeout_ms == 5000 assert config.http.retries == 3 - assert config.claim_forge.base_url == "http://localhost:8001" - assert config.claim_forge.token == "test-token" + assert config.checky_monkey.base_url == "http://localhost:8001" + assert config.checky_monkey.token == "test-token" end test "returns user-friendly error for missing file", %{tmp_dir: tmp_dir} do @@ -173,7 +171,7 @@ defmodule Opsm.ConfigTest do assert {:ok, config} = result # Should use defaults assert config.http.timeout_ms == 3000 - assert config.claim_forge.base_url =~ "7001" + assert config.checky_monkey.base_url =~ "7002" end end end diff --git a/opsm_mobile/api/package_controller.ex b/opsm_mobile/api/package_controller.ex index 1bcf573e..5b08edd8 100644 --- a/opsm_mobile/api/package_controller.ex +++ b/opsm_mobile/api/package_controller.ex @@ -269,10 +269,8 @@ defmodule Opsm.Api.PackageController do # Load OPSM configuration for trust pipeline services # For v1.0, use default configuration config = %OpsmConfig{ - claim_forge: %{base_url: "http://localhost:7001"}, checky_monkey: %{base_url: "http://localhost:7002"}, palimpsest_license: %{base_url: "http://localhost:7003"}, - cicd_hyper_a: %{base_url: "http://localhost:7004"}, oikos: %{base_url: "http://localhost:7005"}, http: %{timeout: 30_000} } diff --git a/runtime/tools/minter/generate-rsr-repo.sh b/runtime/tools/minter/generate-rsr-repo.sh index 37b1b7dd..a126ce8d 100755 --- a/runtime/tools/minter/generate-rsr-repo.sh +++ b/runtime/tools/minter/generate-rsr-repo.sh @@ -199,7 +199,7 @@ trust-scored before they reach your machine. == Evidence - Contract validation: \`nickel typecheck runtime/${TOOL_NAME}.ncl\` -- Integration with OPSM trust pipeline (claim-forge, checky-monkey) +- Integration with OPSM trust pipeline (checky-monkey, palimpsest-license, oikos) - .tool-versions compatibility (reads asdf format) ADOC diff --git a/selur-compose.yml b/selur-compose.yml index 7a38e634..498bca70 100644 --- a/selur-compose.yml +++ b/selur-compose.yml @@ -3,9 +3,9 @@ # Selur-Compose orchestration for OPSM services # # Profiles: -# (default / no profile) — trust pipeline only: opsm + claim-forge + checky-monkey -# + palimpsest + cicd-hyper-a + oikos -# container-security — adds svalinn + selur + vordr + cerro-torre +# (default / no profile) — trust pipeline only: opsm + checky-monkey +# + palimpsest + oikos +# container-security — adds svalinn + selur + vordr # # Usage: # docker compose -f selur-compose.yml up # trust pipeline @@ -53,14 +53,10 @@ networks: - subnet: 172.21.0.0/16 volumes: - claim-forge-data: - driver: local checky-monkey-cache: driver: local palimpsest-db: driver: local - cicd-registry: - driver: local oikos-cache: driver: local opsm-state: @@ -83,10 +79,8 @@ services: environment: - OPSM_ENV=production - OPSM_API_PORT=4466 - - CLAIM_FORGE_URL=http://claim-forge:8080 - CHECKY_MONKEY_URL=http://checky-monkey:8081 - PALIMPSEST_URL=http://palimpsest:8082 - - CICD_HYPER_A_URL=http://cicd-hyper-a:8083 - OIKOS_URL=http://oikos:8084 volumes: - opsm-state:/app/data @@ -96,42 +90,13 @@ services: test: ["CMD", "/app/bin/opsm", "eval", "System.cmd(\"echo\", [\"healthy\"])"] restart: unless-stopped depends_on: - - claim-forge - checky-monkey - palimpsest - - cicd-hyper-a - oikos labels: - "com.opsm.service=api" - "com.opsm.tier=frontend" - # Claim-Forge: Attestation generation - claim-forge: - image: ghcr.io/hyperpolymath/claim-forge:latest - <<: [*security-defaults, *wolfi-base] - container_name: claim-forge - hostname: claim-forge - networks: - - opsm-trust - ports: - - "8080:8080" - environment: - - RUST_LOG=info - - CLAIM_FORGE_PORT=8080 - - SIGNING_KEY_PATH=/secrets/signing.key - volumes: - - claim-forge-data:/data - - ./secrets:/secrets:ro - - /tmp - healthcheck: - <<: *health-defaults - test: ["CMD", "curl", "-f", "http://localhost:8080/health"] - restart: unless-stopped - labels: - - "com.opsm.service=claim-forge" - - "com.opsm.tier=trust" - - "com.opsm.security=high" - # Checky-Monkey: Code verification checky-monkey: image: ghcr.io/hyperpolymath/checky-monkey:latest @@ -189,33 +154,6 @@ services: - "com.opsm.service=palimpsest" - "com.opsm.tier=analysis" - # CICD-Hyper-A: Package registry - cicd-hyper-a: - image: ghcr.io/hyperpolymath/cicd-hyper-a:latest - <<: [*security-defaults, *wolfi-base] - container_name: cicd-hyper-a - hostname: cicd-hyper-a - networks: - - opsm-trust - - opsm-public - ports: - - "8083:8083" - environment: - - ELIXIR_ENV=production - - CICD_PORT=8083 - - REGISTRY_STORAGE=/registry - - FEDERATION_ENABLED=true - volumes: - - cicd-registry:/registry - - /tmp - healthcheck: - <<: *health-defaults - test: ["CMD", "curl", "-f", "http://localhost:8083/health"] - restart: unless-stopped - labels: - - "com.opsm.service=cicd-hyper-a" - - "com.opsm.tier=registry" - # Oikos: Sustainability analysis oikos: image: ghcr.io/hyperpolymath/oikos:latest @@ -331,40 +269,6 @@ services: - "com.opsm.tier=security" - "com.opsm.function=verification" - # Cerro-Torre: Runtime security monitoring (opt-in: --profile container-security) - cerro-torre: - profiles: [container-security] - image: ghcr.io/hyperpolymath/cerro-torre:latest - <<: *wolfi-base - container_name: cerro-torre - hostname: cerro-torre - networks: - - opsm-trust - ports: - - "8088:8088" - environment: - - RUST_LOG=info - - CERRO_PORT=8088 - - FALCO_ENABLED=true - - EBPF_ENABLED=true - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - - /proc:/host/proc:ro - - /sys:/host/sys:ro - - ./cerro-rules:/rules:ro - - /tmp - # Needs host access for eBPF monitoring - privileged: true - pid: host - healthcheck: - <<: *health-defaults - test: ["CMD", "curl", "-f", "http://localhost:8088/health"] - restart: unless-stopped - labels: - - "com.opsm.service=cerro-torre" - - "com.opsm.tier=security" - - "com.opsm.function=monitoring" - # Selur-specific extensions for security x-selur-config: # Global security policies diff --git a/services/cerro-torre/.tool-versions b/services/cerro-torre/.tool-versions deleted file mode 100644 index 3cd07d4b..00000000 --- a/services/cerro-torre/.tool-versions +++ /dev/null @@ -1 +0,0 @@ -rust stable diff --git a/services/cerro-torre/Cargo.lock b/services/cerro-torre/Cargo.lock deleted file mode 100644 index fbca8e0f..00000000 --- a/services/cerro-torre/Cargo.lock +++ /dev/null @@ -1,1852 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "axum" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" -dependencies = [ - "async-trait", - "axum-core", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower 0.5.3", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bitflags" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" - -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "cc" -version = "1.2.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cerro-torre" -version = "1.0.0" -dependencies = [ - "anyhow", - "axum", - "chrono", - "reqwest", - "serde", - "serde_json", - "thiserror 1.0.69", - "tokio", - "tokio-stream", - "tower 0.4.13", - "tower-http 0.5.2", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "http" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "iri-string" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "js-sys" -version = "0.3.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" -dependencies = [ - "cfg-if", - "futures-util", - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mio" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" -dependencies = [ - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64", - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower 0.5.3", - "tower-http 0.6.8", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustc-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" - -[[package]] -name = "rustls" -version = "0.23.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" -dependencies = [ - "itoa", - "serde", - "serde_core", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "tinystr" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.52.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" -dependencies = [ - "bitflags", - "bytes", - "http", - "http-body", - "http-body-util", - "pin-project-lite", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "iri-string", - "pin-project-lite", - "tower 0.5.3", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex-automata", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "web-sys" -version = "0.3.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - -[[package]] -name = "yoke" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zerofrom" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/services/cerro-torre/Cargo.toml b/services/cerro-torre/Cargo.toml deleted file mode 100644 index 2ddfa0a3..00000000 --- a/services/cerro-torre/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -[package] -name = "cerro-torre" -version = "1.0.0" -edition = "2021" -authors = ["Jonathan D.A. Jewell "] -license = "MPL-2.0" -description = "Security monitoring and threat detection service using eBPF and Falco for OPSM" - -[dependencies] -# Web framework -axum = "0.7" -tokio = { version = "1.0", features = ["full"] } -tower = "0.4" -tower-http = { version = "0.5", features = ["trace", "cors"] } - -# Serialization -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" - -# Logging -tracing = "0.1" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } - -# Time -chrono = { version = "0.4", features = ["serde"] } - -# Error handling -anyhow = "1.0" -thiserror = "1.0" - -# HTTP client -reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } - -# Async channels -tokio-stream = "0.1" - -[dev-dependencies] -reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } diff --git a/services/cerro-torre/Containerfile b/services/cerro-torre/Containerfile deleted file mode 100644 index 0b3ffb69..00000000 --- a/services/cerro-torre/Containerfile +++ /dev/null @@ -1,49 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# Cerro-Torre: Runtime security monitoring and threat detection -# eBPF-based monitoring with Falco integration - -FROM cgr.dev/chainguard/rust:latest AS builder - -WORKDIR /build - -COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && \ - echo "fn main() {}" > src/main.rs && \ - cargo build --release && \ - rm -rf src - -COPY src ./src -RUN cargo build --release - -FROM cgr.dev/chainguard/wolfi-base:latest - -# eBPF monitoring tools (falco, bpftrace) are host-kernel facilities -# provided via privileged container mode; not installed as APK packages. -RUN apk add --no-cache \ - libgcc \ - libstdc++ - -RUN addgroup -g 1000 cerro && \ - adduser -D -u 1000 -G cerro cerro - -WORKDIR /app - -COPY --from=builder --chown=cerro:cerro \ - /build/target/release/cerro-torre /app/ - -RUN mkdir -p /rules /logs && chown cerro:cerro /rules /logs - -USER cerro - -EXPOSE 8088 - -HEALTHCHECK --interval=30s --timeout=3s \ - CMD ["/usr/bin/curl", "-f", "http://localhost:8088/health"] - -CMD ["/app/cerro-torre"] - -LABEL org.opencontainers.image.title="Cerro-Torre" \ - org.opencontainers.image.description="Runtime security monitoring" \ - org.opencontainers.image.version="1.0.0" \ - org.opencontainers.image.authors="Jonathan D.A. Jewell " \ - org.opencontainers.image.licenses="MPL-2.0" diff --git a/services/cerro-torre/README.md b/services/cerro-torre/README.md deleted file mode 100644 index 5d14c837..00000000 --- a/services/cerro-torre/README.md +++ /dev/null @@ -1,361 +0,0 @@ - -# Cerro-Torre - Security Monitoring Service - -**Cerro-Torre** is OPSM's runtime security monitoring and threat detection service, providing real-time visibility into container behavior using eBPF and Falco. - -## Features - -- **Falco Integration**: Runtime security monitoring with Falco rules -- **Event Collection**: Captures security-relevant events from containers -- **Severity Classification**: EMERGENCY to DEBUG severity levels -- **Real-time Metrics**: Container monitoring statistics -- **Event Simulation**: Test alerting and workflows -- **REST API**: Query events, metrics, and trigger simulations - -## Quick Start - -### Running Locally - -```bash -# Without Falco (simulated monitoring) -cargo run --release - -# With Falco (real monitoring) -# Install Falco first, then: -cargo run --release -``` - -### Running in Container - -```bash -# Build -podman build -t cerro-torre:latest -f Containerfile . - -# Run with privileged access (required for eBPF) -podman run --privileged -p 8088:8088 cerro-torre:latest -``` - -## API Reference - -### Health Check - -```bash -GET /health -``` - -**Response:** -```json -{ - "status": "healthy", - "version": "1.0.0", - "falco_available": false, - "falco_version": null, - "total_events": 42, - "monitoring_active": false -} -``` - -### Get Security Events - -```bash -POST /events -Content-Type: application/json - -{ - "limit": 100, - "severity": "CRITICAL", - "container": "app-prod" -} -``` - -**Query Parameters:** -- `limit` (optional): Max events to return (default: 100) -- `severity` (optional): Filter by severity level -- `container` (optional): Filter by container name (partial match) - -**Response:** -```json -{ - "events": [ - { - "id": "evt-123456", - "timestamp": "2026-02-05T07:15:00Z", - "severity": "CRITICAL", - "rule": "privilege_escalation_detected", - "message": "Process attempted privilege escalation", - "container_id": "container-abc123", - "container_name": "app-prod", - "process": "su", - "metadata": {} - } - ], - "total": 1, - "critical_count": 1, - "warning_count": 0 -} -``` - -### Get Metrics - -```bash -GET /metrics -``` - -**Response:** -```json -{ - "total_events": 156, - "events_by_severity": { - "emergency": 0, - "alert": 2, - "critical": 5, - "error": 12, - "warning": 45, - "notice": 67, - "informational": 23, - "debug": 2 - }, - "top_rules": [ - { - "rule": "terminal_shell_in_container", - "count": 45 - }, - { - "rule": "write_below_etc", - "count": 23 - } - ], - "monitored_containers": 8 -} -``` - -### Simulate Security Event - -```bash -POST /events/simulate -Content-Type: application/json - -{ - "event_type": "privilege_escalation", - "severity": "CRITICAL", - "container_name": "test-container" -} -``` - -**Response:** -```json -{ - "id": "evt-sim-789", - "timestamp": "2026-02-05T07:16:00Z", - "severity": "CRITICAL", - "rule": "simulated_privilege_escalation", - "message": "Simulated security event: privilege_escalation", - "container_id": "sim-123456", - "container_name": "test-container", - "process": "test-process" -} -``` - -### Clear Events - -```bash -POST /events/clear -``` - -**Response:** `204 No Content` - -## Event Severity Levels - -| Level | Description | Action Required | -|-------|-------------|-----------------| -| **EMERGENCY** | System unusable | Immediate response | -| **ALERT** | Action required immediately | Alert on-call team | -| **CRITICAL** | Critical security violation | Investigate urgently | -| **ERROR** | Error condition | Review and remediate | -| **WARNING** | Warning condition | Monitor closely | -| **NOTICE** | Normal but significant | Log for audit | -| **INFORMATIONAL** | Informational message | Log only | -| **DEBUG** | Debug-level message | Development only | - -## Common Security Rules - -When Falco is active, Cerro-Torre monitors these security events: - -| Rule | Severity | Description | -|------|----------|-------------| -| **privilege_escalation** | CRITICAL | Process gained elevated privileges | -| **write_below_etc** | WARNING | Write to /etc directory | -| **read_sensitive_file** | WARNING | Access to sensitive files (/etc/shadow, SSH keys) | -| **terminal_shell_in_container** | NOTICE | Interactive shell opened | -| **network_connection** | INFORMATIONAL | Outbound network connection | -| **file_opened_for_writing** | DEBUG | File write operation | - -## Examples - -### Monitor for Critical Events - -```bash -# Start monitoring -curl -X POST http://localhost:8088/events \ - -H "Content-Type: application/json" \ - -d '{"severity": "CRITICAL", "limit": 50}' - -# Check metrics -curl http://localhost:8088/metrics | jq '.events_by_severity.critical' -``` - -### Test Alert Pipeline - -```bash -# Simulate critical event -curl -X POST http://localhost:8088/events/simulate \ - -H "Content-Type: application/json" \ - -d '{ - "event_type": "ransomware_detected", - "severity": "EMERGENCY", - "container_name": "production-app" - }' - -# Verify event was captured -curl -X POST http://localhost:8088/events \ - -H "Content-Type: application/json" \ - -d '{"severity": "EMERGENCY", "limit": 1}' | jq . -``` - -### Container-Specific Monitoring - -```bash -# Get all events from specific container -curl -X POST http://localhost:8088/events \ - -H "Content-Type: application/json" \ - -d '{"container": "production", "limit": 100}' \ - | jq '.events[] | {timestamp, severity, rule, message}' -``` - -## Configuration - -Environment variables: - -| Variable | Default | Description | -|----------|---------|-------------| -| `CERRO_TORRE_PORT` | `8088` | HTTP server port | -| `RUST_LOG` | `info` | Log level | - -## Integration with OPSM - -Cerro-Torre completes the OPSM security pipeline: - -```bash -# Full container security workflow -opsm container pipeline ./Containerfile \ - --scan # Svalinn: vulnerability scanning - --sign # Selur: image signing - --verify # Vordr: policy verification - --monitor # Cerro-Torre: runtime monitoring - -# Query runtime security events -opsm container monitor --critical --container myapp -``` - -## Architecture - -``` -┌─────────────────┐ -│ REST API │ -│ (Axum) │ -└────────┬────────┘ - │ - ┌────▼────┐ - │ Falco │ - │ (eBPF) │ - └────┬────┘ - │ - ┌────▼────────┐ - │ Kernel │ - │ Events │ - └─────────────┘ -``` - -## Falco Integration - -When Falco is installed, Cerro-Torre: -1. Monitors kernel syscalls via eBPF -2. Applies Falco security rules -3. Captures security-relevant events -4. Classifies by severity -5. Exposes via REST API - -## Monitoring Flow - -1. **Event Detection**: Falco detects suspicious behavior -2. **Classification**: Event severity determined by rule -3. **Storage**: Event added to in-memory buffer (10,000 events) -4. **API Exposure**: Available via /events endpoint -5. **Metrics**: Aggregated statistics via /metrics - -## Alert Integration - -### Webhook Example - -```bash -# Monitor for critical events and POST to webhook -while true; do - curl -s -X POST http://localhost:8088/events \ - -H "Content-Type: application/json" \ - -d '{"severity": "CRITICAL", "limit": 1}' \ - | jq -r '.events[] | @json' \ - | while read event; do - curl -X POST https://alerts.example.com/webhook \ - -H "Content-Type: application/json" \ - -d "$event" - done - sleep 5 -done -``` - -### Prometheus Metrics - -```bash -# Export metrics to Prometheus format -curl http://localhost:8088/metrics | jq -r ' - .events_by_severity | to_entries[] | - "cerro_torre_events_total{severity=\"\(.key)\"} \(.value)" -' -``` - -## Troubleshooting - -**"Falco not available - running with simulated monitoring"** -- This is informational - simulated mode works for testing -- Install Falco for production: `https://falco.org/docs/getting-started/installation/` - -**"monitoring_active: false"** -- Falco is not installed or not running -- Check: `falco --version` - -**Events buffer full** -- Increase buffer size in code (default: 10,000 events) -- Clear old events: `POST /events/clear` - -**No events captured** -- Verify Falco is running with proper permissions -- Check Falco rules are loaded -- Ensure containers are generating activity - -## Performance - -- **Memory**: ~10MB + (events × 1KB average) -- **CPU**: Minimal (<1% idle, <5% under load) -- **Latency**: <10ms API response time -- **Throughput**: 1000+ events/second - -## License - -MPL-2.0 - -## Author - -Jonathan D.A. Jewell diff --git a/services/cerro-torre/src/main.rs b/services/cerro-torre/src/main.rs deleted file mode 100644 index e1ebfd65..00000000 --- a/services/cerro-torre/src/main.rs +++ /dev/null @@ -1,475 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright (c) Jonathan D.A. Jewell -//! Cerro-Torre: Security monitoring and threat detection service -//! -//! Monitors container runtime security using eBPF and Falco. - -#![forbid(unsafe_code)] -use axum::{ - extract::{Json, State}, - http::StatusCode, - routing::{get, post}, - Router, -}; -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; -use std::collections::VecDeque; -use std::process::Stdio; -use std::sync::Arc; -use tokio::process::Command; -use tokio::sync::RwLock; -use tower_http::trace::TraceLayer; -use tracing::{info, warn}; - -// ============================================================================ -// Types -// ============================================================================ - -#[derive(Clone)] -struct AppState { - falco_available: bool, - event_buffer: Arc>, -} - -struct EventBuffer { - events: VecDeque, - max_size: usize, -} - -impl EventBuffer { - fn new(max_size: usize) -> Self { - Self { - events: VecDeque::with_capacity(max_size), - max_size, - } - } - - fn push(&mut self, event: SecurityEvent) { - if self.events.len() >= self.max_size { - self.events.pop_front(); - } - self.events.push_back(event); - } - - fn get_recent(&self, limit: usize) -> Vec { - self.events - .iter() - .rev() - .take(limit) - .cloned() - .collect() - } - - fn count_by_severity(&self, severity: &EventSeverity) -> usize { - self.events - .iter() - .filter(|e| &e.severity == severity) - .count() - } - - fn clear(&mut self) { - self.events.clear(); - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -struct SecurityEvent { - id: String, - timestamp: DateTime, - severity: EventSeverity, - rule: String, - message: String, - container_id: Option, - container_name: Option, - process: Option, - #[serde(flatten)] - metadata: serde_json::Value, -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "UPPERCASE")] -enum EventSeverity { - Emergency, - Alert, - Critical, - Error, - Warning, - Notice, - Informational, - Debug, -} - -#[derive(Debug, Serialize, Deserialize)] -struct EventsResponse { - events: Vec, - total: usize, - critical_count: usize, - warning_count: usize, -} - -#[derive(Debug, Serialize, Deserialize)] -struct EventQuery { - #[serde(default = "default_limit")] - limit: usize, - #[serde(default)] - severity: Option, - #[serde(default)] - container: Option, -} - -fn default_limit() -> usize { - 100 -} - -#[derive(Debug, Serialize, Deserialize)] -struct MetricsResponse { - total_events: usize, - events_by_severity: SeverityMetrics, - top_rules: Vec, - monitored_containers: usize, -} - -#[derive(Debug, Serialize, Deserialize)] -struct SeverityMetrics { - emergency: usize, - alert: usize, - critical: usize, - error: usize, - warning: usize, - notice: usize, - informational: usize, - debug: usize, -} - -#[derive(Debug, Serialize, Deserialize)] -struct RuleCount { - rule: String, - count: usize, -} - -#[derive(Debug, Serialize, Deserialize)] -struct HealthResponse { - status: String, - version: String, - falco_available: bool, - falco_version: Option, - total_events: usize, - monitoring_active: bool, -} - -#[derive(Debug, Serialize, Deserialize)] -struct SimulateRequest { - event_type: String, - severity: EventSeverity, - #[serde(default)] - container_name: Option, -} - -// ============================================================================ -// Handlers -// ============================================================================ - -async fn health(State(state): State) -> Json { - let falco_version = if state.falco_available { - get_falco_version().await.ok() - } else { - None - }; - - let total_events = state.event_buffer.read().await.events.len(); - - Json(HealthResponse { - status: "healthy".to_string(), - version: env!("CARGO_PKG_VERSION").to_string(), - falco_available: state.falco_available, - falco_version, - total_events, - monitoring_active: state.falco_available, - }) -} - -async fn get_events( - State(state): State, - Json(query): Json, -) -> Json { - let buffer = state.event_buffer.read().await; - - let mut events = buffer.get_recent(query.limit); - - // Filter by severity if specified - if let Some(severity) = &query.severity { - events.retain(|e| &e.severity == severity); - } - - // Filter by container if specified - if let Some(container) = &query.container { - events.retain(|e| { - e.container_name.as_ref().map_or(false, |n| n.contains(container)) - }); - } - - let total = events.len(); - let critical_count = events.iter().filter(|e| matches!( - e.severity, - EventSeverity::Emergency | EventSeverity::Alert | EventSeverity::Critical - )).count(); - let warning_count = events.iter().filter(|e| matches!( - e.severity, - EventSeverity::Warning - )).count(); - - Json(EventsResponse { - events, - total, - critical_count, - warning_count, - }) -} - -async fn get_metrics(State(state): State) -> Json { - let buffer = state.event_buffer.read().await; - - let events_by_severity = SeverityMetrics { - emergency: buffer.count_by_severity(&EventSeverity::Emergency), - alert: buffer.count_by_severity(&EventSeverity::Alert), - critical: buffer.count_by_severity(&EventSeverity::Critical), - error: buffer.count_by_severity(&EventSeverity::Error), - warning: buffer.count_by_severity(&EventSeverity::Warning), - notice: buffer.count_by_severity(&EventSeverity::Notice), - informational: buffer.count_by_severity(&EventSeverity::Informational), - debug: buffer.count_by_severity(&EventSeverity::Debug), - }; - - // Count rules - let mut rule_counts = std::collections::HashMap::new(); - for event in buffer.events.iter() { - *rule_counts.entry(event.rule.clone()).or_insert(0) += 1; - } - - let mut top_rules: Vec = rule_counts - .into_iter() - .map(|(rule, count)| RuleCount { rule, count }) - .collect(); - top_rules.sort_by(|a, b| b.count.cmp(&a.count)); - top_rules.truncate(10); - - // Count unique containers - let monitored_containers = buffer - .events - .iter() - .filter_map(|e| e.container_name.as_ref()) - .collect::>() - .len(); - - Json(MetricsResponse { - total_events: buffer.events.len(), - events_by_severity, - top_rules, - monitored_containers, - }) -} - -async fn clear_events(State(state): State) -> StatusCode { - state.event_buffer.write().await.clear(); - info!("Event buffer cleared"); - StatusCode::NO_CONTENT -} - -async fn simulate_event( - State(state): State, - Json(request): Json, -) -> Result, StatusCode> { - let event = SecurityEvent { - id: uuid::Uuid::new_v4().to_string(), - timestamp: Utc::now(), - severity: request.severity, - rule: format!("simulated_{}", request.event_type), - message: format!("Simulated security event: {}", request.event_type), - container_id: Some("sim-123456".to_string()), - container_name: request.container_name.or_else(|| Some("test-container".to_string())), - process: Some("test-process".to_string()), - metadata: serde_json::json!({ - "simulated": true, - "event_type": request.event_type - }), - }; - - state.event_buffer.write().await.push(event.clone()); - info!("Simulated security event: {}", event.rule); - - Ok(Json(event)) -} - -// ============================================================================ -// Falco Integration -// ============================================================================ - -async fn check_falco_available() -> bool { - Command::new("falco") - .arg("--version") - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .await - .map(|status| status.success()) - .unwrap_or(false) -} - -async fn get_falco_version() -> anyhow::Result { - let output = Command::new("falco") - .arg("--version") - .stdout(Stdio::piped()) - .output() - .await?; - - let version = String::from_utf8(output.stdout)?; - Ok(version - .lines() - .next() - .unwrap_or("unknown") - .trim() - .to_string()) -} - -// ============================================================================ -// Background Monitoring (Mock) -// ============================================================================ - -async fn start_background_monitoring(state: AppState) { - tokio::spawn(async move { - info!("Starting background monitoring"); - - // Generate sample events periodically - let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(30)); - - loop { - interval.tick().await; - - // Simulate detecting a security event - let sample_events = vec![ - ("terminal_shell_in_container", EventSeverity::Notice, "Interactive shell opened in container"), - ("write_below_etc", EventSeverity::Warning, "Write attempt below /etc detected"), - ("read_sensitive_file", EventSeverity::Warning, "Sensitive file access detected"), - ("network_connection", EventSeverity::Informational, "Outbound network connection"), - ]; - - if let Some((rule, severity, message)) = sample_events.get(rand::random::() % sample_events.len()) { - let event = SecurityEvent { - id: uuid::Uuid::new_v4().to_string(), - timestamp: Utc::now(), - severity: severity.clone(), - rule: rule.to_string(), - message: message.to_string(), - container_id: Some(format!("container-{}", rand::random::() % 5)), - container_name: Some(format!("app-{}", rand::random::() % 3)), - process: Some("app".to_string()), - metadata: serde_json::json!({}), - }; - - state.event_buffer.write().await.push(event.clone()); - info!("Detected security event: {} ({})", event.rule, event.severity.to_string()); - } - } - }); -} - -impl ToString for EventSeverity { - fn to_string(&self) -> String { - match self { - EventSeverity::Emergency => "EMERGENCY".to_string(), - EventSeverity::Alert => "ALERT".to_string(), - EventSeverity::Critical => "CRITICAL".to_string(), - EventSeverity::Error => "ERROR".to_string(), - EventSeverity::Warning => "WARNING".to_string(), - EventSeverity::Notice => "NOTICE".to_string(), - EventSeverity::Informational => "INFORMATIONAL".to_string(), - EventSeverity::Debug => "DEBUG".to_string(), - } - } -} - -// ============================================================================ -// UUID generation (simplified) -// ============================================================================ - -mod uuid { - pub struct Uuid; - impl Uuid { - pub fn new_v4() -> Self { - Self - } - pub fn to_string(&self) -> String { - use std::time::SystemTime; - let timestamp = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_nanos(); - format!("{:032x}", timestamp) - } - } -} - -// Random number generator (simplified) -mod rand { - pub fn random() -> T { - T::default() - } -} - -// ============================================================================ -// Main -// ============================================================================ - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - tracing_subscriber::fmt() - .with_env_filter( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")), - ) - .init(); - - info!("Starting Cerro-Torre v{}", env!("CARGO_PKG_VERSION")); - - // Check Falco availability - let falco_available = check_falco_available().await; - info!( - "Falco availability: {}", - if falco_available { "✓" } else { "✗" } - ); - - if !falco_available { - warn!("Falco not available - running with simulated monitoring"); - warn!("Install Falco for real-time security monitoring: https://falco.org/docs/getting-started/installation/"); - } - - // Initialize event buffer (keep last 10000 events) - let event_buffer = Arc::new(RwLock::new(EventBuffer::new(10000))); - - let state = AppState { - falco_available, - event_buffer, - }; - - // Start background monitoring - start_background_monitoring(state.clone()).await; - - let app = Router::new() - .route("/health", get(health)) - .route("/events", post(get_events)) - .route("/events/clear", post(clear_events)) - .route("/events/simulate", post(simulate_event)) - .route("/metrics", get(get_metrics)) - .layer(TraceLayer::new_for_http()) - .with_state(state); - - let port = std::env::var("CERRO_TORRE_PORT").unwrap_or_else(|_| "8088".to_string()); - let addr = format!("0.0.0.0:{}", port); - - info!("Listening on {}", addr); - - let listener = tokio::net::TcpListener::bind(&addr).await?; - axum::serve(listener, app).await?; - - Ok(()) -} diff --git a/services/cicd-hyper-a/.tool-versions b/services/cicd-hyper-a/.tool-versions deleted file mode 100644 index 3cd07d4b..00000000 --- a/services/cicd-hyper-a/.tool-versions +++ /dev/null @@ -1 +0,0 @@ -rust stable diff --git a/services/cicd-hyper-a/Cargo.lock b/services/cicd-hyper-a/Cargo.lock deleted file mode 100644 index 86cad719..00000000 --- a/services/cicd-hyper-a/Cargo.lock +++ /dev/null @@ -1,1212 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "axum" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" -dependencies = [ - "async-trait", - "axum-core", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower 0.5.3", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "bitflags" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" - -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "cc" -version = "1.2.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "cicd-hyper-a" -version = "1.0.0" -dependencies = [ - "anyhow", - "axum", - "chrono", - "serde", - "serde_json", - "tokio", - "tower 0.4.13", - "tower-http", - "tracing", - "tracing-subscriber", - "uuid", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", - "wasip3", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "http" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "bytes", - "http", - "http-body", - "hyper", - "pin-project-lite", - "tokio", - "tower-service", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "indexmap" -version = "2.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", - "serde", - "serde_core", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "js-sys" -version = "0.3.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "libc" -version = "0.2.184" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mio" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" -dependencies = [ - "libc", - "wasi", - "windows-sys", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" -dependencies = [ - "itoa", - "serde", - "serde_core", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "tokio" -version = "1.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys", -] - -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" -dependencies = [ - "bitflags", - "bytes", - "http", - "http-body", - "http-body-util", - "pin-project-lite", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex-automata", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "uuid" -version = "1.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" -dependencies = [ - "getrandom", - "js-sys", - "serde_core", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/services/cicd-hyper-a/Cargo.toml b/services/cicd-hyper-a/Cargo.toml deleted file mode 100644 index 67a1b695..00000000 --- a/services/cicd-hyper-a/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -[package] -name = "cicd-hyper-a" -version = "1.0.0" -edition = "2021" -authors = ["Jonathan D.A. Jewell "] -license = "MPL-2.0" -description = "CI/CD pipeline and package publishing service" - -[dependencies] -# Web framework -axum = "0.7" -tokio = { version = "1.0", features = ["full"] } -tower = "0.4" -tower-http = { version = "0.5", features = ["trace", "cors"] } - -# Serialization -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" - -# Logging -tracing = "0.1" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } - -# Time -chrono = { version = "0.4", features = ["serde"] } - -# Utilities -uuid = { version = "1.0", features = ["v4", "serde"] } -anyhow = "1.0" diff --git a/services/cicd-hyper-a/Containerfile b/services/cicd-hyper-a/Containerfile deleted file mode 100644 index 5d0b5687..00000000 --- a/services/cicd-hyper-a/Containerfile +++ /dev/null @@ -1,41 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# CICD-Hyper-A: CI/CD pipeline and package publishing service -# Rust service with Chainguard Wolfi base - -FROM cgr.dev/chainguard/rust:latest AS builder - -WORKDIR /build - -COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && \ - echo "fn main() {}" > src/main.rs && \ - cargo build --release && \ - rm -rf src - -COPY src ./src -RUN cargo build --release - -FROM cgr.dev/chainguard/wolfi-base:latest - -RUN addgroup -g 1000 cicd && \ - adduser -D -u 1000 -G cicd cicd - -WORKDIR /app - -COPY --from=builder --chown=cicd:cicd \ - /build/target/release/cicd-hyper-a /app/ - -USER cicd - -EXPOSE 8084 - -HEALTHCHECK --interval=30s --timeout=3s \ - CMD ["/app/cicd-hyper-a", "health"] - -CMD ["/app/cicd-hyper-a"] - -LABEL org.opencontainers.image.title="CICD-Hyper-A" \ - org.opencontainers.image.description="CI/CD pipeline and package publishing" \ - org.opencontainers.image.version="1.0.0" \ - org.opencontainers.image.authors="Jonathan D.A. Jewell " \ - org.opencontainers.image.licenses="MPL-2.0" diff --git a/services/cicd-hyper-a/src/main.rs b/services/cicd-hyper-a/src/main.rs deleted file mode 100644 index 14a1a551..00000000 --- a/services/cicd-hyper-a/src/main.rs +++ /dev/null @@ -1,485 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright (c) Jonathan D.A. Jewell -//! CICD-Hyper-A: CI/CD pipeline and package publishing service -//! -//! Handles package publishing, manifest validation, federation sync -//! across registries, and ruleset enforcement. - -#![forbid(unsafe_code)] -use axum::{ - extract::{Json, Path, State}, - http::StatusCode, - routing::{get, post}, - Router, -}; -use chrono::Utc; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::sync::Arc; -use tokio::sync::RwLock; -use tower_http::trace::TraceLayer; -use tracing::info; -use uuid::Uuid; - -// ============================================================================ -// Types -// ============================================================================ - -#[derive(Clone)] -struct AppState { - packages: Arc>>>, - rulesets: Arc>, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -struct PublishedPackage { - name: String, - version: String, - forth: String, - manifest: serde_json::Value, - attestations: Vec, - published_at: String, - published_by: String, - federation_status: FederationStatus, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -struct FederationStatus { - synced_registries: Vec, - pending_registries: Vec, - failed_registries: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -struct FederationFailure { - registry: String, - error: String, - last_attempt: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -struct Ruleset { - id: String, - name: String, - description: String, - rules: Vec, - severity: RuleSeverity, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -struct Rule { - id: String, - check: String, - message: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -enum RuleSeverity { - Error, - Warning, - Info, -} - -#[derive(Debug, Deserialize)] -struct PublishRequest { - name: String, - version: String, - forth: String, - manifest: serde_json::Value, - #[serde(default)] - attestations: Vec, - #[serde(default)] - target_registries: Vec, -} - -#[derive(Debug, Serialize)] -struct PublishResponse { - publish_id: String, - name: String, - version: String, - status: String, - federation_status: FederationStatus, - published_at: String, -} - -#[derive(Debug, Deserialize)] -struct ValidateRequest { - manifest: serde_json::Value, - #[serde(default)] - rulesets: Vec, -} - -#[derive(Debug, Serialize)] -struct ValidateResponse { - valid: bool, - errors: Vec, - warnings: Vec, - rulesets_applied: Vec, -} - -#[derive(Debug, Serialize)] -struct ValidationIssue { - rule_id: String, - ruleset: String, - message: String, - field: Option, -} - -#[derive(Debug, Serialize)] -struct HealthResponse { - status: String, - version: String, - packages_published: usize, - rulesets_loaded: usize, -} - -// ============================================================================ -// Handlers -// ============================================================================ - -async fn health(State(state): State) -> Json { - let packages = state.packages.read().await; - let total: usize = packages.values().map(|v| v.len()).sum(); - - Json(HealthResponse { - status: "healthy".to_string(), - version: env!("CARGO_PKG_VERSION").to_string(), - packages_published: total, - rulesets_loaded: state.rulesets.len(), - }) -} - -async fn publish_package( - State(state): State, - Json(request): Json, -) -> Result<(StatusCode, Json), (StatusCode, String)> { - info!("Publishing {}@{} to @{}", request.name, request.version, request.forth); - - let publish_id = Uuid::new_v4().to_string(); - let now = Utc::now().to_rfc3339(); - - // Validate manifest before publishing - let validation = validate_manifest(&request.manifest, &state.rulesets, &[]); - if !validation.valid { - return Err(( - StatusCode::BAD_REQUEST, - format!( - "Manifest validation failed: {}", - validation - .errors - .iter() - .map(|e| e.message.as_str()) - .collect::>() - .join(", ") - ), - )); - } - - let federation = FederationStatus { - synced_registries: vec![request.forth.clone()], - pending_registries: request.target_registries.clone(), - failed_registries: vec![], - }; - - let pkg = PublishedPackage { - name: request.name.clone(), - version: request.version.clone(), - forth: request.forth.clone(), - manifest: request.manifest, - attestations: request.attestations, - published_at: now.clone(), - published_by: "opsm-cli".to_string(), - federation_status: federation.clone(), - }; - - let mut packages = state.packages.write().await; - packages - .entry(request.name.clone()) - .or_insert_with(Vec::new) - .push(pkg); - - Ok(( - StatusCode::CREATED, - Json(PublishResponse { - publish_id, - name: request.name, - version: request.version, - status: "published".to_string(), - federation_status: federation, - published_at: now, - }), - )) -} - -async fn query_package_version( - State(state): State, - Path((name, version)): Path<(String, String)>, -) -> Result, (StatusCode, String)> { - let packages = state.packages.read().await; - - packages - .get(&name) - .and_then(|versions| versions.iter().find(|v| v.version == version)) - .cloned() - .map(Json) - .ok_or(( - StatusCode::NOT_FOUND, - format!("Package {}@{} not found", name, version), - )) -} - -async fn query_package( - State(state): State, - Path(name): Path, -) -> Result>, (StatusCode, String)> { - let packages = state.packages.read().await; - - packages - .get(&name) - .cloned() - .map(Json) - .ok_or((StatusCode::NOT_FOUND, format!("Package {} not found", name))) -} - -async fn validate( - State(state): State, - Json(request): Json, -) -> Json { - info!("Validating manifest against {} rulesets", request.rulesets.len()); - let result = validate_manifest(&request.manifest, &state.rulesets, &request.rulesets); - Json(result) -} - -async fn list_rulesets(State(state): State) -> Json> { - Json(state.rulesets.as_ref().clone()) -} - -async fn get_ruleset( - State(state): State, - Path(ruleset_id): Path, -) -> Result, (StatusCode, String)> { - state - .rulesets - .iter() - .find(|r| r.id == ruleset_id) - .cloned() - .map(Json) - .ok_or(( - StatusCode::NOT_FOUND, - format!("Ruleset {} not found", ruleset_id), - )) -} - -async fn federation_status( - State(state): State, - Path((name, version)): Path<(String, String)>, -) -> Result, (StatusCode, String)> { - let packages = state.packages.read().await; - - packages - .get(&name) - .and_then(|versions| versions.iter().find(|v| v.version == version)) - .map(|pkg| Json(pkg.federation_status.clone())) - .ok_or(( - StatusCode::NOT_FOUND, - format!("Package {}@{} not found", name, version), - )) -} - -// ============================================================================ -// Validation engine -// ============================================================================ - -fn validate_manifest( - manifest: &serde_json::Value, - all_rulesets: &[Ruleset], - requested: &[String], -) -> ValidateResponse { - let applicable: Vec<&Ruleset> = if requested.is_empty() { - all_rulesets.iter().collect() - } else { - all_rulesets - .iter() - .filter(|r| requested.contains(&r.id)) - .collect() - }; - - let mut errors = Vec::new(); - let mut warnings = Vec::new(); - let rulesets_applied: Vec = applicable.iter().map(|r| r.id.clone()).collect(); - - for ruleset in &applicable { - for rule in &ruleset.rules { - let issue = check_rule(manifest, rule, &ruleset.id); - if let Some(issue) = issue { - match ruleset.severity { - RuleSeverity::Error => errors.push(issue), - RuleSeverity::Warning | RuleSeverity::Info => warnings.push(issue), - } - } - } - } - - ValidateResponse { - valid: errors.is_empty(), - errors, - warnings, - rulesets_applied, - } -} - -fn check_rule( - manifest: &serde_json::Value, - rule: &Rule, - ruleset_id: &str, -) -> Option { - match rule.check.as_str() { - "has_name" => { - if manifest.get("name").and_then(|v| v.as_str()).is_none() { - Some(ValidationIssue { - rule_id: rule.id.clone(), - ruleset: ruleset_id.to_string(), - message: rule.message.clone(), - field: Some("name".to_string()), - }) - } else { - None - } - } - "has_version" => { - if manifest.get("version").and_then(|v| v.as_str()).is_none() { - Some(ValidationIssue { - rule_id: rule.id.clone(), - ruleset: ruleset_id.to_string(), - message: rule.message.clone(), - field: Some("version".to_string()), - }) - } else { - None - } - } - "has_license" => { - if manifest.get("license").and_then(|v| v.as_str()).is_none() { - Some(ValidationIssue { - rule_id: rule.id.clone(), - ruleset: ruleset_id.to_string(), - message: rule.message.clone(), - field: Some("license".to_string()), - }) - } else { - None - } - } - "has_description" => { - if manifest - .get("description") - .and_then(|v| v.as_str()) - .is_none() - { - Some(ValidationIssue { - rule_id: rule.id.clone(), - ruleset: ruleset_id.to_string(), - message: rule.message.clone(), - field: Some("description".to_string()), - }) - } else { - None - } - } - _ => None, - } -} - -// ============================================================================ -// Default rulesets -// ============================================================================ - -fn build_default_rulesets() -> Vec { - vec![ - Ruleset { - id: "opsm-required".to_string(), - name: "OPSM Required Fields".to_string(), - description: "Mandatory fields for OPSM package publishing".to_string(), - rules: vec![ - Rule { - id: "R001".to_string(), - check: "has_name".to_string(), - message: "Package must have a name".to_string(), - }, - Rule { - id: "R002".to_string(), - check: "has_version".to_string(), - message: "Package must have a version".to_string(), - }, - ], - severity: RuleSeverity::Error, - }, - Ruleset { - id: "opsm-recommended".to_string(), - name: "OPSM Recommended Fields".to_string(), - description: "Recommended metadata for better discoverability".to_string(), - rules: vec![ - Rule { - id: "W001".to_string(), - check: "has_license".to_string(), - message: "Package should specify a license".to_string(), - }, - Rule { - id: "W002".to_string(), - check: "has_description".to_string(), - message: "Package should have a description".to_string(), - }, - ], - severity: RuleSeverity::Warning, - }, - ] -} - -// ============================================================================ -// Main -// ============================================================================ - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - tracing_subscriber::fmt() - .with_env_filter( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")), - ) - .init(); - - info!("Starting CICD-Hyper-A v{}", env!("CARGO_PKG_VERSION")); - - let state = AppState { - packages: Arc::new(RwLock::new(HashMap::new())), - rulesets: Arc::new(build_default_rulesets()), - }; - - info!("{} rulesets loaded", state.rulesets.len()); - - let app = Router::new() - .route("/health", get(health)) - .route("/packages/publish", post(publish_package)) - .route("/packages/{name}/{version}", get(query_package_version)) - .route("/packages/{name}", get(query_package)) - .route( - "/packages/{name}/{version}/federation", - get(federation_status), - ) - .route("/validate", post(validate)) - .route("/rulesets", get(list_rulesets)) - .route("/rulesets/{ruleset_id}", get(get_ruleset)) - .layer(TraceLayer::new_for_http()) - .with_state(state); - - let port = std::env::var("CICD_HYPER_A_PORT").unwrap_or_else(|_| "8084".to_string()); - let addr = format!("0.0.0.0:{}", port); - - info!("Listening on {}", addr); - - let listener = tokio::net::TcpListener::bind(&addr).await?; - axum::serve(listener, app).await?; - - Ok(()) -} diff --git a/services/claim-forge/.tool-versions b/services/claim-forge/.tool-versions deleted file mode 100644 index 3cd07d4b..00000000 --- a/services/claim-forge/.tool-versions +++ /dev/null @@ -1 +0,0 @@ -rust stable diff --git a/services/claim-forge/Cargo.lock b/services/claim-forge/Cargo.lock deleted file mode 100644 index 906e4c3b..00000000 --- a/services/claim-forge/Cargo.lock +++ /dev/null @@ -1,2335 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "axum" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" -dependencies = [ - "async-trait", - "axum-core", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower 0.5.3", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - -[[package]] -name = "bitflags" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "cc" -version = "1.2.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chacha20" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", - "rand_core 0.10.1", -] - -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "claim-forge" -version = "1.0.0" -dependencies = [ - "anyhow", - "axum", - "base64", - "chrono", - "ed25519-dalek", - "hex", - "rand 0.10.1", - "reqwest", - "serde", - "serde_json", - "sha3", - "thiserror 1.0.69", - "tokio", - "tower 0.4.13", - "tower-http 0.5.2", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core 0.6.4", - "serde", - "sha2", - "subtle", - "zeroize", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi 5.3.0", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" -dependencies = [ - "cfg-if", - "libc", - "r-efi 6.0.0", - "rand_core 0.10.1", - "wasip2", - "wasip3", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "http" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.0", - "serde", - "serde_core", -] - -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "iri-string" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "js-sys" -version = "0.3.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" -dependencies = [ - "cfg-if", - "futures-util", - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "keccak" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" -dependencies = [ - "cpufeatures 0.2.17", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mio" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" -dependencies = [ - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.4", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha", - "rand_core 0.9.5", -] - -[[package]] -name = "rand" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" -dependencies = [ - "chacha20", - "getrandom 0.4.2", - "rand_core 0.10.1", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rand_core" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64", - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower 0.5.3", - "tower-http 0.6.8", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustc-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustls" -version = "0.23.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" -dependencies = [ - "itoa", - "serde", - "serde_core", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest", -] - -[[package]] -name = "sha3" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" -dependencies = [ - "digest", - "keccak", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "tinystr" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.52.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" -dependencies = [ - "bitflags", - "bytes", - "http", - "http-body", - "http-body-util", - "pin-project-lite", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "iri-string", - "pin-project-lite", - "tower 0.5.3", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex-automata", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "typenum" -version = "1.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - -[[package]] -name = "web-sys" -version = "0.3.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - -[[package]] -name = "yoke" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zerofrom" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/services/claim-forge/Cargo.toml b/services/claim-forge/Cargo.toml deleted file mode 100644 index b50d3e12..00000000 --- a/services/claim-forge/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -[package] -name = "claim-forge" -version = "1.0.0" -edition = "2021" -authors = ["Jonathan D.A. Jewell "] -license = "MPL-2.0" -description = "Cryptographic attestation generation service for OPSM" - -[dependencies] -# Web framework -axum = "0.7" -tokio = { version = "1.0", features = ["full"] } -tower = "0.4" -tower-http = { version = "0.5", features = ["trace", "cors"] } - -# Serialization -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" - -# Cryptography -ed25519-dalek = { version = "2.0", features = ["rand_core"] } -sha3 = "0.10" -rand = "0.10" -hex = "0.4" -base64 = "0.22" - -# Logging -tracing = "0.1" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } - -# Time -chrono = { version = "0.4", features = ["serde"] } - -# Error handling -anyhow = "1.0" -thiserror = "1.0" - -[dev-dependencies] -reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } diff --git a/services/claim-forge/Containerfile b/services/claim-forge/Containerfile deleted file mode 100644 index c6f9eec6..00000000 --- a/services/claim-forge/Containerfile +++ /dev/null @@ -1,50 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# Claim-Forge: Cryptographic attestation generation service -# Rust service with Chainguard Wolfi base - -FROM cgr.dev/chainguard/rust:latest AS builder - -WORKDIR /build - -# Copy manifests -COPY Cargo.toml Cargo.lock ./ - -# Cache dependencies -RUN mkdir src && \ - echo "fn main() {}" > src/main.rs && \ - cargo build --release && \ - rm -rf src - -# Copy source -COPY src ./src - -# Build release -RUN cargo build --release - -# Runtime stage -FROM cgr.dev/chainguard/wolfi-base:latest - -RUN apk add --no-cache libgcc libstdc++ - -RUN addgroup -g 1000 claimforge && \ - adduser -D -u 1000 -G claimforge claimforge - -WORKDIR /app - -COPY --from=builder --chown=claimforge:claimforge \ - /build/target/release/claim-forge /app/ - -USER claimforge - -EXPOSE 8080 - -HEALTHCHECK --interval=30s --timeout=3s \ - CMD ["/app/claim-forge", "health"] - -CMD ["/app/claim-forge", "serve"] - -LABEL org.opencontainers.image.title="Claim-Forge" \ - org.opencontainers.image.description="Cryptographic attestation generation" \ - org.opencontainers.image.version="1.0.0" \ - org.opencontainers.image.authors="Jonathan D.A. Jewell " \ - org.opencontainers.image.licenses="MPL-2.0" diff --git a/services/claim-forge/src/main.rs b/services/claim-forge/src/main.rs deleted file mode 100644 index 018bec58..00000000 --- a/services/claim-forge/src/main.rs +++ /dev/null @@ -1,229 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright (c) Jonathan D.A. Jewell -//! Claim-Forge: Cryptographic attestation generation service -//! -//! Generates SLSA provenance attestations and signs artifacts with Ed25519. - -#![forbid(unsafe_code)] -use axum::{ - extract::{Json, State}, - http::StatusCode, - routing::{get, post}, - Router, -}; -use chrono::Utc; -use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey}; -use rand::rngs::OsRng; -use serde::{Deserialize, Serialize}; -use sha3::{Digest, Sha3_512}; -use std::sync::Arc; -use tower_http::trace::TraceLayer; -use tracing::{info, warn}; - -// ============================================================================ -// Types -// ============================================================================ - -#[derive(Clone)] -struct AppState { - signing_key: Arc, - verifying_key: Arc, -} - -#[derive(Debug, Serialize, Deserialize)] -struct AttestationRequest { - artifact_path: String, - artifact_digest: String, - claim_type: ClaimType, - metadata: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -enum ClaimType { - BuildProvenance, - CodeReview, - SecurityScan, - LicenseCheck, -} - -#[derive(Debug, Serialize, Deserialize)] -struct AttestationResponse { - attestation_uri: String, - signature: String, - public_key: String, - digest: String, - timestamp: String, -} - -#[derive(Debug, Serialize, Deserialize)] -struct VerifyRequest { - attestation_uri: String, - signature: String, - public_key: String, - digest: String, -} - -#[derive(Debug, Serialize, Deserialize)] -struct VerifyResponse { - verified: bool, - message: String, -} - -#[derive(Debug, Serialize, Deserialize)] -struct HealthResponse { - status: String, - version: String, - uptime: u64, -} - -// ============================================================================ -// Handlers -// ============================================================================ - -async fn health() -> Json { - Json(HealthResponse { - status: "healthy".to_string(), - version: env!("CARGO_PKG_VERSION").to_string(), - uptime: 0, - }) -} - -async fn generate_attestation( - State(state): State, - Json(request): Json, -) -> Result, (StatusCode, String)> { - info!( - "Generating attestation for: {} (type: {:?})", - request.artifact_path, request.claim_type - ); - - let attestation = create_attestation(&request); - let signature = sign_attestation(&state.signing_key, &attestation); - let attestation_uri = format!("opsm://attestations/{}", hex::encode(&attestation[..16])); - - let response = AttestationResponse { - attestation_uri, - signature: hex::encode(signature.to_bytes()), - public_key: hex::encode(state.verifying_key.to_bytes()), - digest: request.artifact_digest.clone(), - timestamp: Utc::now().to_rfc3339(), - }; - - info!("Attestation generated successfully"); - Ok(Json(response)) -} - -async fn verify_attestation( - State(state): State, - Json(request): Json, -) -> Result, (StatusCode, String)> { - info!("Verifying attestation: {}", request.attestation_uri); - - let signature_bytes = hex::decode(&request.signature) - .map_err(|e| (StatusCode::BAD_REQUEST, format!("Invalid signature hex: {}", e)))?; - - let signature = Signature::from_bytes( - &signature_bytes - .try_into() - .map_err(|_| (StatusCode::BAD_REQUEST, "Invalid signature length".to_string()))?, - ); - - let pubkey_bytes = hex::decode(&request.public_key) - .map_err(|e| (StatusCode::BAD_REQUEST, format!("Invalid public key hex: {}", e)))?; - - let verifying_key = VerifyingKey::from_bytes( - &pubkey_bytes - .try_into() - .map_err(|_| (StatusCode::BAD_REQUEST, "Invalid public key length".to_string()))?, - ) - .map_err(|e| (StatusCode::BAD_REQUEST, format!("Invalid public key: {}", e)))?; - - let message = request.digest.as_bytes(); - let verified = verifying_key.verify_strict(message, &signature).is_ok(); - - let response = VerifyResponse { - verified, - message: if verified { - "Attestation signature verified successfully".to_string() - } else { - "Attestation signature verification failed".to_string() - }, - }; - - if verified { - info!("Attestation verified successfully"); - } else { - warn!("Attestation verification failed"); - } - - Ok(Json(response)) -} - -// ============================================================================ -// Helpers -// ============================================================================ - -fn create_attestation(request: &AttestationRequest) -> Vec { - let provenance = serde_json::json!({ - "artifact": request.artifact_path, - "digest": request.artifact_digest, - "claim_type": format!("{:?}", request.claim_type), - "metadata": request.metadata, - "timestamp": Utc::now().to_rfc3339(), - "issuer": "claim-forge/1.0.0", - }); - - provenance.to_string().into_bytes() -} - -fn sign_attestation(signing_key: &SigningKey, attestation: &[u8]) -> Signature { - let mut hasher = Sha3_512::new(); - hasher.update(attestation); - let hash = hasher.finalize(); - signing_key.sign(&hash) -} - -// ============================================================================ -// Main -// ============================================================================ - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - tracing_subscriber::fmt() - .with_env_filter( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")), - ) - .init(); - - info!("Starting Claim-Forge v{}", env!("CARGO_PKG_VERSION")); - - let mut csprng = OsRng; - let signing_key = SigningKey::generate(&mut csprng); - let verifying_key = signing_key.verifying_key(); - - info!("Public key: {}", hex::encode(verifying_key.to_bytes())); - - let state = AppState { - signing_key: Arc::new(signing_key), - verifying_key: Arc::new(verifying_key), - }; - - let app = Router::new() - .route("/health", get(health)) - .route("/attestation/generate", post(generate_attestation)) - .route("/attestation/verify", post(verify_attestation)) - .layer(TraceLayer::new_for_http()) - .with_state(state); - - let port = std::env::var("CLAIM_FORGE_PORT").unwrap_or_else(|_| "8080".to_string()); - let addr = format!("0.0.0.0:{}", port); - - info!("Listening on {}", addr); - - let listener = tokio::net::TcpListener::bind(&addr).await?; - axum::serve(listener, app).await?; - - Ok(()) -} diff --git a/stapeln.toml b/stapeln.toml index a2859267..82cbe085 100644 --- a/stapeln.toml +++ b/stapeln.toml @@ -8,7 +8,7 @@ # - opsm_ex/ Elixir OTP application (core API + CLI) # - native/ Rust NIFs (pq_crypto, quic_transport, opsm_pq_nif) # - runtime/ Nickel plugin configs (83 core plugins) + Zig shim dispatcher -# - services/ Satellite services (selur, vordr, svalinn, claim-forge, etc.) +# - services/ Satellite services (selur, vordr, svalinn, etc.) [metadata] name = "opsm" @@ -194,7 +194,7 @@ seccomp-profile = "default" [security.signing] algorithm = "ML-DSA-87" -provider = "cerro-torre" +provider = "selur" [security.sbom] format = "spdx-json" @@ -236,15 +236,6 @@ env = { LOG_LEVEL = "info" } # orchestrated by selur-compose.yml. Each uses Chainguard Wolfi # and inherits the same security posture. -[satellites.claim-forge] -description = "Attestation generation service" -image = "ghcr.io/hyperpolymath/claim-forge:latest" -ports = [8080] -networks = ["opsm-trust"] -env = { RUST_LOG = "info", CLAIM_FORGE_PORT = "8080" } -volumes = ["claim-forge-data:/data"] -healthcheck = { cmd = ["curl", "-f", "http://localhost:8080/health"], interval = "30s", timeout = "10s", retries = 3 } - [satellites.checky-monkey] description = "Code verification and fuzzing service" image = "ghcr.io/hyperpolymath/checky-monkey:latest" @@ -264,15 +255,6 @@ env = { GLEAM_ENV = "production", PALIMPSEST_PORT = "8082" } volumes = ["palimpsest-db:/data"] healthcheck = { cmd = ["curl", "-f", "http://localhost:8082/health"], interval = "30s", timeout = "10s", retries = 3 } -[satellites.cicd-hyper-a] -description = "Federated package registry" -image = "ghcr.io/hyperpolymath/cicd-hyper-a:latest" -ports = [8083] -networks = ["opsm-trust", "opsm-public"] -env = { ELIXIR_ENV = "production", CICD_PORT = "8083", FEDERATION_ENABLED = "true" } -volumes = ["cicd-registry:/registry"] -healthcheck = { cmd = ["curl", "-f", "http://localhost:8083/health"], interval = "30s", timeout = "10s", retries = 3 } - [satellites.oikos] description = "Sustainability analysis service" image = "ghcr.io/hyperpolymath/oikos:latest" @@ -309,14 +291,6 @@ env = { RUST_LOG = "info", VORDR_PORT = "8087" } volumes = ["vordr-policies:/policies"] healthcheck = { cmd = ["curl", "-f", "http://localhost:8087/health"], interval = "30s", timeout = "10s", retries = 3 } -[satellites.cerro-torre] -description = "Runtime security monitoring (eBPF + Falco)" -image = "ghcr.io/hyperpolymath/cerro-torre:latest" -ports = [8088] -networks = ["opsm-trust"] -env = { RUST_LOG = "info", CERRO_PORT = "8088", FALCO_ENABLED = "true", EBPF_ENABLED = "true" } -healthcheck = { cmd = ["curl", "-f", "http://localhost:8088/health"], interval = "30s", timeout = "10s", retries = 3 } - # ── Networks ─────────────────────────────────────────────────── [networks.opsm-trust] @@ -333,10 +307,8 @@ subnet = "172.21.0.0/16" [volumes] opsm-state = { driver = "local", description = "OPSM application state and cache" } -claim-forge-data = { driver = "local", description = "Attestation store" } checky-monkey-cache = { driver = "local", description = "Verification cache" } palimpsest-db = { driver = "local", description = "License database" } -cicd-registry = { driver = "local", description = "Package registry storage" } oikos-cache = { driver = "local", description = "Sustainability analysis cache" } svalinn-db = { driver = "local", description = "Vulnerability database cache" } vordr-policies = { driver = "local", description = "Runtime verification policies" }