From 3bd398ab4c01a46a63015b90689a31e26c1cb714 Mon Sep 17 00:00:00 2001 From: Jon Langevin Date: Sat, 3 Oct 2026 03:10:55 -0400 Subject: [PATCH] feat: add signed recovery contracts Tasks 10 and 11: bind bootstrap, install receipt, and managed lifecycle data to immutable identities, digests, epochs, and stored authority. Export shared rejection vectors without moving host policy into core. --- go.mod | 133 +---- go.sum | 282 +++------ plugin.json | 2 +- .../protocol/v1/bootstrap.proto | 38 ++ protocol/bootstrap.go | 378 +++++++++++++ protocol/bootstrap_test.go | 269 +++++++++ protocol/managed_lifecycle.go | 391 +++++++++++++ protocol/managed_lifecycle_test.go | 231 ++++++++ protocol/pb/bootstrap.pb.go | 382 +++++++++++++ protocol/recovery_vectors_test.go | 229 ++++++++ .../testdata/recovery_contract_vectors.json | 533 ++++++++++++++++++ 11 files changed, 2543 insertions(+), 325 deletions(-) create mode 100644 proto/workflow_plugin_compute_core/protocol/v1/bootstrap.proto create mode 100644 protocol/bootstrap.go create mode 100644 protocol/bootstrap_test.go create mode 100644 protocol/managed_lifecycle.go create mode 100644 protocol/managed_lifecycle_test.go create mode 100644 protocol/pb/bootstrap.pb.go create mode 100644 protocol/recovery_vectors_test.go create mode 100644 protocol/testdata/recovery_contract_vectors.json diff --git a/go.mod b/go.mod index 4e36bae..263c407 100644 --- a/go.mod +++ b/go.mod @@ -1,152 +1,45 @@ module github.com/GoCodeAlone/workflow-plugin-compute-core -go 1.26.0 +go 1.26.5 require ( - github.com/GoCodeAlone/workflow v0.62.0 - google.golang.org/grpc v1.81.1 + github.com/GoCodeAlone/workflow v0.86.0 + google.golang.org/grpc v1.83.2 google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af ) require ( github.com/BurntSushi/toml v1.6.0 // indirect - github.com/DataDog/datadog-go/v5 v5.8.3 // indirect github.com/GoCodeAlone/go-plugin v1.7.0 // indirect - github.com/GoCodeAlone/modular v1.13.0 // indirect - github.com/GoCodeAlone/modular/modules/auth v1.15.0 // indirect - github.com/GoCodeAlone/modular/modules/eventbus/v2 v2.8.0 // indirect + github.com/GoCodeAlone/modular v1.13.6 // indirect github.com/GoCodeAlone/yaegi v0.17.2 // indirect - github.com/IBM/sarama v1.47.0 // indirect - github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/aws/aws-sdk-go-v2 v1.41.6 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect - github.com/aws/aws-sdk-go-v2/config v1.32.16 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.19.15 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 // indirect - github.com/aws/aws-sdk-go-v2/service/kinesis v1.43.4 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.0.10 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.16 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.42.0 // indirect - github.com/aws/smithy-go v1.25.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/cenkalti/backoff/v5 v5.0.3 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudevents/sdk-go/v2 v2.16.2 // indirect - github.com/containerd/errdefs v1.0.0 // indirect - github.com/containerd/errdefs/pkg v0.3.0 // indirect - github.com/containerd/log v0.1.0 // indirect - github.com/danieljoos/wincred v1.2.3 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/distribution/reference v0.6.0 // indirect - github.com/docker/docker v28.5.2+incompatible // indirect - github.com/docker/go-connections v0.7.0 // indirect - github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/eapache/go-resiliency v1.7.0 // indirect - github.com/eapache/queue v1.1.0 // indirect - github.com/expr-lang/expr v1.17.8 // indirect github.com/fatih/color v1.19.0 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/go-jose/go-jose/v4 v4.1.4 // indirect - github.com/go-logr/logr v1.4.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/godbus/dbus/v5 v5.2.2 // indirect - github.com/golang-jwt/jwt/v5 v5.3.1 // indirect + github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/golobby/cast v1.3.3 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-retryablehttp v0.7.8 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 // indirect - github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect - github.com/hashicorp/go-sockaddr v1.0.7 // indirect - github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/golang-lru v1.0.2 // indirect - github.com/hashicorp/hcl v1.0.1-vault-7 // indirect - github.com/hashicorp/vault/api v1.23.0 // indirect github.com/hashicorp/yamux v0.1.2 // indirect - github.com/itchyny/gojq v0.12.18 // indirect - github.com/itchyny/timefmt-go v0.1.7 // indirect - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgx/v5 v5.9.1 // indirect - github.com/jackc/puddle/v2 v2.2.2 // indirect - github.com/jcmturner/aescts/v2 v2.0.0 // indirect - github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect - github.com/jcmturner/gofork v1.7.6 // indirect - github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect - github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect - github.com/klauspost/compress v1.18.6 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-colorable v0.1.15 // indirect github.com/mattn/go-isatty v0.0.22 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/sys/sequential v0.6.0 // indirect - github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/nats-io/nats.go v1.52.0 // indirect - github.com/nats-io/nkeys v0.4.15 // indirect - github.com/nats-io/nuid v1.0.1 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect github.com/oklog/run v1.2.0 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/pierrec/lz4/v4 v4.1.26 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.23.2 // indirect - github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.67.5 // indirect - github.com/prometheus/procfs v0.20.1 // indirect - github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect - github.com/redis/go-redis/v9 v9.19.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/ryanuber/go-glob v1.0.0 // indirect - github.com/xdg-go/pbkdf2 v1.0.0 // indirect - github.com/xdg-go/scram v1.2.0 // indirect - github.com/xdg-go/stringprep v1.0.4 // indirect - github.com/zalando/go-keyring v0.2.8 // indirect - go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect - go.opentelemetry.io/otel v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect - go.opentelemetry.io/otel/sdk v1.43.0 // indirect - go.opentelemetry.io/otel/trace v1.43.0 // indirect - go.opentelemetry.io/proto/otlp v1.10.0 // indirect - go.uber.org/atomic v1.11.0 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect - go.yaml.in/yaml/v2 v2.4.4 // indirect - golang.org/x/crypto v0.51.0 // indirect - golang.org/x/net v0.54.0 // indirect - golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.44.0 // indirect - golang.org/x/text v0.37.0 // indirect - golang.org/x/time v0.15.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260511170946-3700d4141b60 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 // indirect + golang.org/x/net v0.58.0 // indirect + golang.org/x/sys v0.48.0 // indirect + golang.org/x/text v0.42.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260630182238-925bb5da69e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/libc v1.70.0 // indirect + modernc.org/libc v1.73.4 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect - modernc.org/sqlite v1.47.0 // indirect + modernc.org/sqlite v1.53.0 // indirect ) diff --git a/go.sum b/go.sum index 2b69e90..621e4ec 100644 --- a/go.sum +++ b/go.sum @@ -1,30 +1,23 @@ -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DataDog/datadog-go/v5 v5.8.3 h1:s58CUJ9s8lezjhTNJO/SxkPBv2qZjS3ktpRSqGF5n0s= github.com/DataDog/datadog-go/v5 v5.8.3/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= github.com/GoCodeAlone/go-plugin v1.7.0 h1:EwnhqPlXiNmp85S+MXnKKvm3YlfA6O4NzBb4+GSlEVY= github.com/GoCodeAlone/go-plugin v1.7.0/go.mod h1:HbGQRZUIa+jbDfjsaZIMJYvrz+LnxL0mJpggfynSTMk= -github.com/GoCodeAlone/modular v1.13.0 h1:UfsegfAmPWcPYQOqYZFsw/LNySBmMDcthiOQe5bscqE= -github.com/GoCodeAlone/modular v1.13.0/go.mod h1:b06Pvgcc8HsGxvl30iO39zGH2jIWz467QEj2+OQL2Do= -github.com/GoCodeAlone/modular/modules/auth v1.15.0 h1:pBSkPSf4k4GLSbUQFLuPa+nFbfoJXGzSz9q89VoapZk= -github.com/GoCodeAlone/modular/modules/auth v1.15.0/go.mod h1:vmIm/LQrcURS2p02YwaELb+CZoHPtT0XB0v1i+sj9i4= -github.com/GoCodeAlone/modular/modules/eventbus/v2 v2.8.0 h1:buYs0TGNbAZgtTq1Qb+dfmTv3+ZOBIN0HbvVBLyNqxE= -github.com/GoCodeAlone/modular/modules/eventbus/v2 v2.8.0/go.mod h1:329flAKmwrPq2JEwu9iltWv6A83H/Di82Xze+kvdKDw= -github.com/GoCodeAlone/workflow v0.62.0 h1:emFkTomDpVmBcEfw7quRO4V/J4qDsWNx/CrBdlGqkfg= -github.com/GoCodeAlone/workflow v0.62.0/go.mod h1:659GGDrw3QJ7b625y9rf8QhKIpt1VCoEG0MxKu5tGQs= +github.com/GoCodeAlone/modular v1.13.6 h1:AIdfdR/KTPHX8T5KJBXUHMokliutUHjl6jRfkqdh/7Q= +github.com/GoCodeAlone/modular v1.13.6/go.mod h1:Vs92YltRNqA/v3peoHdqHTCjD10ww7rY3iURf1Ju450= +github.com/GoCodeAlone/modular/modules/auth v1.19.0 h1:ughD47BqEAhg7+3trucwcs5KzEpq9gIS/6jY4nkeGtA= +github.com/GoCodeAlone/modular/modules/auth v1.19.0/go.mod h1:pGPazN7Irlza8kUxE7WsXri44PhURKYBGym1tlBUl+k= +github.com/GoCodeAlone/modular/modules/eventbus/v2 v2.10.0 h1:2ljVafd/1LYchF47WrnA1+ji8mcmVXMJ4F5qDrhZZi4= +github.com/GoCodeAlone/modular/modules/eventbus/v2 v2.10.0/go.mod h1:AKLcRGsw5gp2Q1zhuK0TBnMJOsaRe3aJ2OKnLFE2O5w= +github.com/GoCodeAlone/workflow v0.86.0 h1:9NatmWg2120RHDFesltawOAS7jJvzyJ4k9Q+5WueySg= +github.com/GoCodeAlone/workflow v0.86.0/go.mod h1:WaH+HhX8lrmoGu/vQjkyloZTydUWZVVRo9KFJsZAUtI= github.com/GoCodeAlone/yaegi v0.17.2 h1:WK6Y6e0t1a6U7r+S2dN3CGWW1PizYD3zO0zneToZPxM= github.com/GoCodeAlone/yaegi v0.17.2/go.mod h1:z5Pr6Wse6QJcQvpgxTxzMAevFarH0N37TG88Y9dprx0= -github.com/IBM/sarama v1.47.0 h1:GcQFEd12+KzfPYeLgN69Fh7vLCtYRhVIx0rO4TZO318= -github.com/IBM/sarama v1.47.0/go.mod h1:7gLLIU97nznOmA6TX++Qds+DRxH89P2XICY2KAQUzAY= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/IBM/sarama v1.50.3 h1:zpY2iZYmt+z+0Bo3aYF+cD48OBt2hIgiDPZUuZKTXcc= +github.com/IBM/sarama v1.50.3/go.mod h1:Jo4MSfdDT3ycmQj7/ab8eLZwnvwCKZm/8H7SCbtyo8U= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/alicebob/miniredis/v2 v2.36.1 h1:Dvc5oAnNOr7BIfPn7tF269U8DvRW1dBG2D5n0WrfYMI= -github.com/alicebob/miniredis/v2 v2.36.1/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= -github.com/antithesishq/antithesis-sdk-go v0.5.0-default-no-op h1:Ucf+QxEKMbPogRO5guBNe5cgd9uZgfoJLOYs8WWhtjM= -github.com/antithesishq/antithesis-sdk-go v0.5.0-default-no-op/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E= github.com/aws/aws-sdk-go-v2 v1.41.6 h1:1AX0AthnBQzMx1vbmir3Y4WsnJgiydmnJjiLu+LvXOg= github.com/aws/aws-sdk-go-v2 v1.41.6/go.mod h1:dy0UzBIfwSeot4grGvY1AqFWN5zgziMmWGzysDnHFcQ= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o= @@ -45,8 +38,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 h1:HtOTYcb github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8/go.mod h1:VsK9abqQeGlzPgUr+isNWzPlK2vKe9INMLWnY65f5Xs= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 h1:PUmZeJU6Y1Lbvt9WFuJ0ugUK2xn6hIWUBBbKuOWF30s= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22/go.mod h1:nO6egFBoAaoXze24a2C0NjQCvdpk8OueRoYimvEB9jo= -github.com/aws/aws-sdk-go-v2/service/kinesis v1.43.4 h1:3m9iJtMtLq75jKRAfw0kapoHUlbzi0CRVigysBN/FHA= -github.com/aws/aws-sdk-go-v2/service/kinesis v1.43.4/go.mod h1:O2L6vGm4xacEuN2otHFMgn7yXXlgzFKzxrba0fy/yk8= +github.com/aws/aws-sdk-go-v2/service/kinesis v1.43.5 h1:LxgRVyuY+5DEPSX7kmin/V7toE8MWZ9U8n2dqRtX+RE= +github.com/aws/aws-sdk-go-v2/service/kinesis v1.43.5/go.mod h1:eUebEBEqVfOwEyDDDbGauH4PNqDCuepRvTaNbJeWr5w= github.com/aws/aws-sdk-go-v2/service/signin v1.0.10 h1:a1Fq/KXn75wSzoJaPQTgZO0wHGqE9mjFnylnqEPTchA= github.com/aws/aws-sdk-go-v2/service/signin v1.0.10/go.mod h1:p6+MXNxW7IA6dMgHfTAzljuwSKD0NCm/4lbS4t6+7vI= github.com/aws/aws-sdk-go-v2/service/sso v1.30.16 h1:x6bKbmDhsgSZwv6q19wY/u3rLk/3FGjJWyqKcIRufpE= @@ -59,10 +52,6 @@ github.com/aws/smithy-go v1.25.0 h1:Sz/XJ64rwuiKtB6j98nDIPyYrV1nVNJ4YU74gttcl5U= github.com/aws/smithy-go v1.25.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= -github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bufbuild/protocompile v0.10.0 h1:+jW/wnLMLxaCEG8AX9lD0bQ5v9h1RUiMKOBOT5ll9dM= github.com/bufbuild/protocompile v0.10.0/go.mod h1:G9qQIQo0xZ6Uyj6CMNz0saGmx2so+KONo8/KrELABiY= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= @@ -73,12 +62,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudevents/sdk-go/v2 v2.16.2 h1:ZYDFrYke4FD+jM8TZTJJO6JhKHzOQl2oqpFK1D+NnQM= github.com/cloudevents/sdk-go/v2 v2.16.2/go.mod h1:laOcGImm4nVJEU+PHnUrKL56CKmRL65RlQF0kRmW/kg= -github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= -github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= -github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= @@ -92,65 +75,46 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= -github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= -github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= -github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eapache/go-resiliency v1.7.0 h1:n3NRTnBn5N0Cbi/IeOHuQn9s2UwVUH7Ga0ZWcP+9JTA= github.com/eapache/go-resiliency v1.7.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= -github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/expr-lang/expr v1.17.8 h1:W1loDTT+0PQf5YteHSTpju2qfUfNoBt4yw9+wOEU9VM= github.com/expr-lang/expr v1.17.8/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc= +github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE= +github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= +github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= -github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-openapi/jsonpointer v0.24.0 h1:AA6mCjHYHmZ+1RU2Js089EaOK/iwXXNwQsTgnsTha2M= +github.com/go-openapi/jsonpointer v0.24.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y= github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golobby/cast v1.3.3 h1:s2Lawb9RMz7YyYf8IrfMQY4IFmA1R/lgfmj97Vc6fig= github.com/golobby/cast v1.3.3/go.mod h1:0oDO5IT84HTXcbLDf1YXuk0xtg/cRDrxhbpWKxwtJCY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= -github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -173,7 +137,6 @@ github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9 github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= @@ -186,16 +149,16 @@ github.com/hashicorp/vault/api v1.23.0 h1:gXgluBsSECfRWTSW9niY2jwg2e9mMJc4WoHNv4 github.com/hashicorp/vault/api v1.23.0/go.mod h1:zransKiB9ftp+kgY8ydjnvCU7Wk8i9L0DYWpXeMj9ko= github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= -github.com/itchyny/gojq v0.12.18 h1:gFGHyt/MLbG9n6dqnvlliiya2TaMMh6FFaR2b1H6Drc= -github.com/itchyny/gojq v0.12.18/go.mod h1:4hPoZ/3lN9fDL1D+aK7DY1f39XZpY9+1Xpjz8atrEkg= -github.com/itchyny/timefmt-go v0.1.7 h1:xyftit9Tbw+Dc/huSSPJaEmX1TVL8lw5vxjJLK4GMMA= -github.com/itchyny/timefmt-go v0.1.7/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI= +github.com/itchyny/gojq v0.12.19 h1:ttXA0XCLEMoaLOz5lSeFOZ6u6Q3QxmG46vfgI4O0DEs= +github.com/itchyny/gojq v0.12.19/go.mod h1:5galtVPDywX8SPSOrqjGxkBeDhSxEW1gSxoy7tn1iZY= +github.com/itchyny/timefmt-go v0.1.8 h1:1YEo1JvfXeAHKdjelbYr/uCuhkybaHCeTkH8Bo791OI= +github.com/itchyny/timefmt-go v0.1.8/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.9.1 h1:uwrxJXBnx76nyISkhr33kQLlUqjv7et7b9FjCen/tdc= -github.com/jackc/pgx/v5 v5.9.1/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/pgx/v5 v5.10.0 h1:VhSvgU2jSli8o3AqIEOTJr7rZwAEUVo4E4XhR94Zfr0= +github.com/jackc/pgx/v5 v5.10.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= @@ -204,8 +167,6 @@ github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8 github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= -github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= -github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= @@ -214,10 +175,8 @@ github.com/jhump/protoreflect v1.16.0 h1:54fZg+49widqXYQ0b+usAFHbMkBGR4PpXrsHc8+ github.com/jhump/protoreflect v1.16.0/go.mod h1:oYPd7nPvcBw/5wlDfm/AVmU9zH9BgqGCI469pGxfj/8= github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 h1:9Nu54bhS/H/Kgo2/7xNSUuC5G28VR8ljfrLKU2G4IjU= github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12/go.mod h1:TBzl5BIHNXfS9+C35ZyJaklL7mLDbgUkcgXzSLa8Tk0= -github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= -github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ= +github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -225,65 +184,41 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= +github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= -github.com/minio/highwayhash v1.0.4-0.20251030100505-070ab1a87a76 h1:KGuD/pM2JpL9FAYvBrnBBeENKZNh6eNtjqytV6TYjnk= -github.com/minio/highwayhash v1.0.4-0.20251030100505-070ab1a87a76/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= -github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= -github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= -github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= -github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= -github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= -github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ= -github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/nats-io/jwt/v2 v2.8.0 h1:K7uzyz50+yGZDO5o772eRE7atlcSEENpL7P+b74JV1g= -github.com/nats-io/jwt/v2 v2.8.0/go.mod h1:me11pOkwObtcBNR8AiMrUbtVOUGkqYjMQZ6jnSdVUIA= -github.com/nats-io/nats-server/v2 v2.12.4 h1:ZnT10v2LU2Xcoiy8ek9X6Se4YG8EuMfIfvAEuFVx1Ts= -github.com/nats-io/nats-server/v2 v2.12.4/go.mod h1:5MCp/pqm5SEfsvVZ31ll1088ZTwEUdvRX1Hmh/mTTDg= github.com/nats-io/nats.go v1.52.0 h1:n3avV4VBsCgsdwh71TppsTwtv+QdPs7ntSKM8qJLGsc= github.com/nats-io/nats.go v1.52.0/go.mod h1:26HypzazeOkyO3/mqd1zZd53STJN0EjCYF9Uy2ZOBno= -github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4= -github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs= +github.com/nats-io/nkeys v0.4.16 h1:rd5oAuLOb8mnAycB0xleuEBNS1pVVnN0fv/FF34Eypg= +github.com/nats-io/nkeys v0.4.16/go.mod h1:llLgWoI0o4z/Q57q2R1kHfmocyhGV6VG/U18Glg1Afs= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E= github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= -github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pierrec/lz4/v4 v4.1.26 h1:GrpZw1gZttORinvzBdXPUXATeqlJjqUG/D87TKMnhjY= -github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= +github.com/pierrec/lz4/v4 v4.1.27 h1:+PhzhWDrjRj89TH2sw43nE3+4+W8lSxIuQadEHZyjUk= +github.com/pierrec/lz4/v4 v4.1.27/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -297,8 +232,8 @@ github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEy github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k= -github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA= +github.com/redis/go-redis/v9 v9.21.0 h1:FPBE4hhbAke+TLmcY3WkpbDffJEomdqPn3HYiqAtL9E= +github.com/redis/go-redis/v9 v9.21.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -308,9 +243,6 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -318,10 +250,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -336,40 +264,32 @@ github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs= github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= -github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPccHs= github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0= -github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= -github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= -go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= @@ -378,115 +298,69 @@ go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= -golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= -golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= -golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M= +golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA= +golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c= +golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= +golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= -golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= +golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= +golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= +golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= -golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= +golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/genproto/googleapis/api v0.0.0-20260511170946-3700d4141b60 h1:3WsB1FAbiRIf2tOxscWKs3pQBD9he1NsrnbhMuWfekc= -google.golang.org/genproto/googleapis/api v0.0.0-20260511170946-3700d4141b60/go.mod h1:7yoXV7RIh5gblj/xVYoogxAWvA9wUeVbpsK/M694l00= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 h1:seT2EwLWM78plQ7wcDfuWBc/4FAEAXDDiaSol4ku4qo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= -google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7 h1:jQ9p21COKWjP3VwuFrNRiiOTMh3mPpN45R7SLrH/HUU= +google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7/go.mod h1:KqHwBx2upmfa1XSi1WuRvC+2VGCLtooKkfmyvRbUmqA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260630182238-925bb5da69e7 h1:eM/YSd5bBFagF51o1E745Ta7RwzpW0h+z+QDNZOgmQ8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260630182238-925bb5da69e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= +google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI= google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= -gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= -modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= -modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw= -modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0= +modernc.org/cc/v4 v4.28.4 h1:Hd/4Es+MBj+/7hSdZaisNyu6bv3V0Dp2MdllyfqaH+c= +modernc.org/cc/v4 v4.28.4/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI= +modernc.org/ccgo/v4 v4.34.4 h1:OVnSOWQjVKOYkFxoHYB+qQmSHK5gqMqARM+K9DpR/Ws= +modernc.org/ccgo/v4 v4.34.4/go.mod h1:qdKqE8FNIYyysougB1RX9MxCzp5oJOcQXSobANJ4TuE= modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo= -modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= +modernc.org/gc/v3 v3.1.3 h1:6QAplYyVO+KdPW3pGnqmJDUxtkec8ooEWvks/hhU3lc= +modernc.org/gc/v3 v3.1.3/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= -modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw= -modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo= +modernc.org/libc v1.73.4 h1:+ra4Ui8ngyt8HDcO1FTDPWlkAh6yOdaO2yAoh8MddQA= +modernc.org/libc v1.73.4/go.mod h1:DXZ3eO8qMCNn2SnmTNCiC71nJ9Rcq3PsnpU6Vc4rWK8= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= -modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg= +modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.47.0 h1:R1XyaNpoW4Et9yly+I2EeX7pBza/w+pmYee/0HJDyKk= -modernc.org/sqlite v1.47.0/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig= +modernc.org/sqlite v1.53.0 h1:20WG8N9q4ji/dEqGk4uiI0c6OPjSeLTNYGFCc3+7c1M= +modernc.org/sqlite v1.53.0/go.mod h1:xoEpOIpGrgT48H5iiyt/YXPCZPEzlfmfFwtk8Lklw8s= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/plugin.json b/plugin.json index 8208d5a..d6c7f2c 100644 --- a/plugin.json +++ b/plugin.json @@ -7,7 +7,7 @@ "type": "external", "tier": "community", "private": false, - "minEngineVersion": "0.64.7", + "minEngineVersion": "0.86.0", "keywords": [ "compute", "protocol", diff --git a/proto/workflow_plugin_compute_core/protocol/v1/bootstrap.proto b/proto/workflow_plugin_compute_core/protocol/v1/bootstrap.proto new file mode 100644 index 0000000..a428826 --- /dev/null +++ b/proto/workflow_plugin_compute_core/protocol/v1/bootstrap.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +package workflow_plugin_compute_core.protocol.v1; + +option go_package = "github.com/GoCodeAlone/workflow-plugin-compute-core/protocol/pb;pb"; + +// Signature-free, domain-bound bytes for the ephemeral install receipt key. +message AgentSetupInstallReceiptPayload { + string domain = 1; + string protocol_version = 2; + string receipt_id = 3; + string challenge = 4; + string invite_id = 5; + string install_session_id = 6; + string worker_id = 7; + string package_id = 8; + string package_version = 9; + string package_digest = 10; + string manifest_digest = 11; + uint64 bootstrap_epoch = 12; + uint64 package_epoch = 13; + uint64 keyset_epoch = 14; + string launcher_digest = 15; + string slot_id = 16; + string slot_digest = 17; + string service_unit_digest = 18; + string service_mode = 19; + int64 started_pid = 20; + string heartbeat_id = 21; + int64 heartbeat_at_unix_nano = 22; + string cloud_final_unit = 23; + string scrub_unit = 24; + string wrapper_hash = 25; + uint64 scan_hit_count = 26; + string code_digest = 27; + int64 completed_at_unix_nano = 28; + int64 expires_at_unix_nano = 29; +} diff --git a/protocol/bootstrap.go b/protocol/bootstrap.go new file mode 100644 index 0000000..5752d56 --- /dev/null +++ b/protocol/bootstrap.go @@ -0,0 +1,378 @@ +package protocol + +import ( + "crypto/ed25519" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "net/url" + "strings" + "time" + + "github.com/GoCodeAlone/workflow-plugin-compute-core/protocol/pb" + "google.golang.org/protobuf/proto" +) + +const ( + ComputeBootstrapManifestSchema = "compute-bootstrap-manifest.v1" + AgentSetupInstallReceiptSchema = "agent-setup-install-receipt.v1" +) + +type BootstrapAssetRef struct { + URL string `json:"url"` + Digest string `json:"digest"` +} + +type BootstrapPlatformAsset struct { + OS string `json:"os"` + Arch string `json:"arch"` + Asset BootstrapAssetRef `json:"asset"` +} + +type BootstrapSigningKey struct { + KeyID string `json:"key_id"` + PublicKey string `json:"public_key"` +} + +// ComputeBootstrapManifest is canonical JSON signed by an independently pinned +// bootstrap root. PackageSigningKeys are package roots, never bootstrap roots. +type ComputeBootstrapManifest struct { + ProtocolVersion string `json:"protocol_version"` + SchemaVersion string `json:"schema_version"` + Version string `json:"version"` + ServerOrigin string `json:"server_origin"` + WFCTLVersion string `json:"wfctl_version"` + WFCTLAssets []BootstrapPlatformAsset `json:"wfctl_assets"` + PluginComputeVersion string `json:"plugin_compute_version"` + PluginComputeArchive BootstrapAssetRef `json:"plugin_compute_archive"` + Bootstrap BootstrapAssetRef `json:"bootstrap"` + PackageID string `json:"package_id"` + PackageVersion string `json:"package_version"` + PackageManifestDigest string `json:"package_manifest_digest"` + BootstrapEpoch uint64 `json:"bootstrap_epoch"` + PackageEpoch uint64 `json:"package_epoch"` + KeysetEpoch uint64 `json:"keyset_epoch"` + KeysetDigest string `json:"keyset_digest"` + PackageSigningKeys []BootstrapSigningKey `json:"package_signing_keys"` + ExpiresAt time.Time `json:"expires_at"` +} + +func (m ComputeBootstrapManifest) validate() error { + var errs []error + errs = append(errs, recoveryProtocolError(m.ProtocolVersion)) + if m.SchemaVersion != ComputeBootstrapManifestSchema { + errs = append(errs, errors.New("schema_version must identify the compute bootstrap manifest")) + } + for _, field := range []struct{ name, value string }{ + {"version", m.Version}, {"wfctl_version", m.WFCTLVersion}, {"plugin_compute_version", m.PluginComputeVersion}, + {"package_id", m.PackageID}, {"package_version", m.PackageVersion}, + } { + errs = append(errs, validateIdentifier(field.name, field.value)) + } + u, err := recoveryHTTPSURL(m.ServerOrigin) + if err != nil || u.Path != "" { + errs = append(errs, errors.New("server_origin must be an HTTPS origin without path, credentials, query, or fragment")) + } + if len(m.WFCTLAssets) == 0 { + errs = append(errs, errors.New("wfctl_assets is required")) + } + seen := make(map[string]bool) + for i, asset := range m.WFCTLAssets { + prefix := fmt.Sprintf("wfctl_assets[%d]", i) + errs = append(errs, validateIdentifier(prefix+".os", asset.OS), validateIdentifier(prefix+".arch", asset.Arch), validateBootstrapAsset(prefix+".asset", asset.Asset)) + platform := asset.OS + "/" + asset.Arch + if seen[platform] { + errs = append(errs, errors.New("wfctl_assets must not repeat a platform")) + } + seen[platform] = true + } + errs = append(errs, validateBootstrapAsset("plugin_compute_archive", m.PluginComputeArchive), validateBootstrapAsset("bootstrap", m.Bootstrap)) + errs = append(errs, validateRequiredSHA256Digest("package_manifest_digest", m.PackageManifestDigest)...) + errs = append(errs, validateRequiredSHA256Digest("keyset_digest", m.KeysetDigest)...) + if m.BootstrapEpoch == 0 || m.PackageEpoch == 0 || m.KeysetEpoch == 0 { + errs = append(errs, errors.New("bootstrap_epoch, package_epoch, and keyset_epoch must be positive")) + } + if len(m.PackageSigningKeys) == 0 { + errs = append(errs, errors.New("package_signing_keys is required")) + } + seenKeys := make(map[string]bool) + for _, key := range m.PackageSigningKeys { + public, err := base64.StdEncoding.DecodeString(key.PublicKey) + if err != nil || len(public) != ed25519.PublicKeySize || key.KeyID != Ed25519KeyID(public) { + errs = append(errs, errors.New("package_signing_keys must contain Ed25519 public keys with matching SHA256 key IDs")) + } + if seenKeys[key.KeyID] { + errs = append(errs, errors.New("package_signing_keys must not repeat a key ID")) + } + seenKeys[key.KeyID] = true + } + if m.ExpiresAt.IsZero() { + errs = append(errs, errors.New("expires_at is required")) + } + return errors.Join(errs...) +} + +func (m ComputeBootstrapManifest) ValidateAt(now time.Time) error { + return errors.Join(m.validate(), recoveryExpiryError(m.ExpiresAt, now)) +} + +func (m ComputeBootstrapManifest) CanonicalBytes() ([]byte, error) { + if err := m.validate(); err != nil { + return nil, err + } + m.ExpiresAt = m.ExpiresAt.UTC() + return json.Marshal(m) +} + +func (m ComputeBootstrapManifest) Sign(key ed25519.PrivateKey) (SignatureEnvelope, error) { + data, err := m.CanonicalBytes() + if err != nil { + return SignatureEnvelope{}, err + } + return signRecoveryBytes(data, key) +} + +func (m ComputeBootstrapManifest) Verify(signature SignatureEnvelope, public ed25519.PublicKey, now time.Time) error { + if err := m.ValidateAt(now); err != nil { + return err + } + data, err := m.CanonicalBytes() + if err != nil { + return err + } + return verifyRecoveryBytes(data, signature, public) +} + +type AgentSetupInstallReceipt struct { + ProtocolVersion string `json:"protocol_version"` + ReceiptID string `json:"receipt_id"` + Challenge string `json:"challenge"` + InviteID string `json:"invite_id"` + InstallSessionID string `json:"install_session_id"` + WorkerID string `json:"worker_id"` + PackageID string `json:"package_id"` + PackageVersion string `json:"package_version"` + PackageDigest string `json:"package_digest"` + ManifestDigest string `json:"manifest_digest"` + BootstrapEpoch uint64 `json:"bootstrap_epoch"` + PackageEpoch uint64 `json:"package_epoch"` + KeysetEpoch uint64 `json:"keyset_epoch"` + LauncherDigest string `json:"launcher_digest"` + SlotID string `json:"slot_id"` + SlotDigest string `json:"slot_digest"` + ServiceUnitDigest string `json:"service_unit_digest"` + ServiceMode string `json:"service_mode"` + StartedPID int64 `json:"started_pid"` + HeartbeatID string `json:"heartbeat_id"` + HeartbeatAt time.Time `json:"heartbeat_at"` + CloudFinalUnit string `json:"cloud_final_unit"` + ScrubUnit string `json:"scrub_unit"` + WrapperHash string `json:"wrapper_hash"` + ScanHitCount uint64 `json:"scan_hit_count"` + CodeDigest string `json:"code_digest"` + CompletedAt time.Time `json:"completed_at"` + ExpiresAt time.Time `json:"expires_at"` + Signature SignatureEnvelope `json:"signature,omitzero"` +} + +// AgentSetupInstallReceiptBinding is supplied from stored setup authority, not +// from the receipt or bearer token. Challenge consumption remains host-owned. +type AgentSetupInstallReceiptBinding struct { + Challenge string `json:"challenge"` + InviteID string `json:"invite_id"` + InstallSessionID string `json:"install_session_id"` + WorkerID string `json:"worker_id"` + PackageID string `json:"package_id"` + PackageVersion string `json:"package_version"` + PackageDigest string `json:"package_digest"` + ManifestDigest string `json:"manifest_digest"` + BootstrapEpoch uint64 `json:"bootstrap_epoch"` + PackageEpoch uint64 `json:"package_epoch"` + KeysetEpoch uint64 `json:"keyset_epoch"` + ExpiresAt time.Time `json:"expires_at"` +} + +func (r AgentSetupInstallReceipt) Binding() AgentSetupInstallReceiptBinding { + return AgentSetupInstallReceiptBinding{ + Challenge: r.Challenge, InviteID: r.InviteID, InstallSessionID: r.InstallSessionID, WorkerID: r.WorkerID, + PackageID: r.PackageID, PackageVersion: r.PackageVersion, PackageDigest: r.PackageDigest, ManifestDigest: r.ManifestDigest, + BootstrapEpoch: r.BootstrapEpoch, PackageEpoch: r.PackageEpoch, KeysetEpoch: r.KeysetEpoch, ExpiresAt: r.ExpiresAt.UTC(), + } +} + +func (r AgentSetupInstallReceipt) validate() error { + var errs []error + errs = append(errs, recoveryProtocolError(r.ProtocolVersion)) + for _, field := range []struct{ name, value string }{ + {"receipt_id", r.ReceiptID}, {"invite_id", r.InviteID}, {"install_session_id", r.InstallSessionID}, {"worker_id", r.WorkerID}, + {"package_id", r.PackageID}, {"package_version", r.PackageVersion}, {"service_mode", r.ServiceMode}, + {"heartbeat_id", r.HeartbeatID}, {"cloud_final_unit", r.CloudFinalUnit}, {"scrub_unit", r.ScrubUnit}, + } { + errs = append(errs, validateIdentifier(field.name, field.value)) + } + challenge, err := base64.StdEncoding.DecodeString(r.Challenge) + if err != nil || len(challenge) != 32 { + errs = append(errs, errors.New("challenge must encode 256 bits")) + } + for _, field := range []struct{ name, value string }{ + {"package_digest", r.PackageDigest}, {"manifest_digest", r.ManifestDigest}, {"launcher_digest", r.LauncherDigest}, + {"slot_digest", r.SlotDigest}, {"service_unit_digest", r.ServiceUnitDigest}, {"wrapper_hash", r.WrapperHash}, {"code_digest", r.CodeDigest}, + } { + errs = append(errs, validateRequiredSHA256Digest(field.name, field.value)...) + } + if r.BootstrapEpoch == 0 || r.PackageEpoch == 0 || r.KeysetEpoch == 0 { + errs = append(errs, errors.New("bootstrap_epoch, package_epoch, and keyset_epoch must be positive")) + } + if r.SlotID != "A" && r.SlotID != "B" { + errs = append(errs, errors.New("slot_id must identify A or B")) + } + if r.StartedPID <= 0 || r.ScanHitCount != 0 { + errs = append(errs, errors.New("started_pid must be positive and scan_hit_count must be zero")) + } + for _, field := range []struct { + name string + value time.Time + }{ + {"heartbeat_at", r.HeartbeatAt}, {"completed_at", r.CompletedAt}, {"expires_at", r.ExpiresAt}, + } { + if field.value.IsZero() || !timeFromUnixNano(unixNanoOrZero(field.value)).Equal(field.value) { + errs = append(errs, fmt.Errorf("%s must be a nonzero nanosecond timestamp", field.name)) + } + } + if r.HeartbeatAt.After(r.CompletedAt) || !r.CompletedAt.Before(r.ExpiresAt) { + errs = append(errs, errors.New("heartbeat_at must not exceed completed_at, which must precede expires_at")) + } + return errors.Join(errs...) +} + +func (r AgentSetupInstallReceipt) ValidateAt(now time.Time) error { + var future error + if r.CompletedAt.After(now) { + future = errors.New("completed_at must not be in the future") + } + return errors.Join(r.validate(), recoveryExpiryError(r.ExpiresAt, now), future) +} + +func (r AgentSetupInstallReceipt) SigningPayload() *pb.AgentSetupInstallReceiptPayload { + return &pb.AgentSetupInstallReceiptPayload{ + Domain: AgentSetupInstallReceiptSchema, ProtocolVersion: r.ProtocolVersion, ReceiptId: r.ReceiptID, Challenge: r.Challenge, + InviteId: r.InviteID, InstallSessionId: r.InstallSessionID, WorkerId: r.WorkerID, PackageId: r.PackageID, PackageVersion: r.PackageVersion, + PackageDigest: r.PackageDigest, ManifestDigest: r.ManifestDigest, BootstrapEpoch: r.BootstrapEpoch, PackageEpoch: r.PackageEpoch, KeysetEpoch: r.KeysetEpoch, + LauncherDigest: r.LauncherDigest, SlotId: r.SlotID, SlotDigest: r.SlotDigest, ServiceUnitDigest: r.ServiceUnitDigest, ServiceMode: r.ServiceMode, + StartedPid: r.StartedPID, HeartbeatId: r.HeartbeatID, HeartbeatAtUnixNano: unixNanoOrZero(r.HeartbeatAt), CloudFinalUnit: r.CloudFinalUnit, ScrubUnit: r.ScrubUnit, + WrapperHash: r.WrapperHash, ScanHitCount: r.ScanHitCount, CodeDigest: r.CodeDigest, CompletedAtUnixNano: unixNanoOrZero(r.CompletedAt), ExpiresAtUnixNano: unixNanoOrZero(r.ExpiresAt), + } +} + +func (r AgentSetupInstallReceipt) SigningBytes() ([]byte, error) { + if err := r.validate(); err != nil { + return nil, err + } + return proto.MarshalOptions{Deterministic: true}.Marshal(r.SigningPayload()) +} + +func UnmarshalAgentSetupInstallReceiptPayloadStrict(data []byte) (*pb.AgentSetupInstallReceiptPayload, error) { + var payload pb.AgentSetupInstallReceiptPayload + if err := proto.Unmarshal(data, &payload); err != nil { + return nil, err + } + if err := rejectUnknownProtoFields(&payload); err != nil { + return nil, err + } + if payload.Domain != AgentSetupInstallReceiptSchema || payload.ProtocolVersion != Version { + return nil, errors.New("receipt payload domain/protocol is not supported") + } + return &payload, nil +} + +func (r *AgentSetupInstallReceipt) Sign(key ed25519.PrivateKey) error { + data, err := r.SigningBytes() + if err != nil { + return err + } + r.Signature, err = signRecoveryBytes(data, key) + return err +} + +func (r AgentSetupInstallReceipt) Verify(public ed25519.PublicKey, expected AgentSetupInstallReceiptBinding, now time.Time) error { + if err := r.ValidateAt(now); err != nil { + return err + } + expected.ExpiresAt = expected.ExpiresAt.UTC() + if r.Binding() != expected { + return errors.New("install receipt does not match the stored setup binding") + } + data, err := r.SigningBytes() + if err != nil { + return err + } + return verifyRecoveryBytes(data, r.Signature, public) +} + +func Ed25519KeyID(public ed25519.PublicKey) string { + if len(public) != ed25519.PublicKeySize { + return "" + } + sum := sha256.Sum256(public) + return "sha256:" + hex.EncodeToString(sum[:]) +} + +func signRecoveryBytes(data []byte, key ed25519.PrivateKey) (SignatureEnvelope, error) { + if len(key) != ed25519.PrivateKeySize { + return SignatureEnvelope{}, errors.New("Ed25519 private key has invalid size") + } + return SignatureEnvelope{Algorithm: "ed25519", KeyID: Ed25519KeyID(key.Public().(ed25519.PublicKey)), Value: base64.StdEncoding.EncodeToString(ed25519.Sign(key, data))}, nil +} + +func verifyRecoveryBytes(data []byte, signature SignatureEnvelope, public ed25519.PublicKey) error { + if len(public) != ed25519.PublicKeySize || signature.Algorithm != "ed25519" || signature.KeyID != Ed25519KeyID(public) { + return errors.New("signature must use the expected Ed25519 public-key fingerprint") + } + decoded, err := base64.StdEncoding.DecodeString(signature.Value) + if err != nil || len(decoded) != ed25519.SignatureSize || base64.StdEncoding.EncodeToString(decoded) != signature.Value || !ed25519.Verify(public, data, decoded) { + return errors.New("Ed25519 signature is invalid") + } + return nil +} + +func recoveryProtocolError(version string) error { + if version != Version { + return errors.New("protocol_version is not supported") + } + return nil +} + +func recoveryExpiryError(expires, now time.Time) error { + if now.IsZero() || expires.IsZero() || !now.Before(expires) { + return errors.New("validation time must be nonzero and precede expires_at") + } + return nil +} + +func recoveryHTTPSURL(value string) (*url.URL, error) { + u, err := url.Parse(value) + if err != nil || u.Scheme != "https" || u.Hostname() == "" || u.User != nil || u.RawQuery != "" || u.ForceQuery || u.Fragment != "" || strings.TrimSpace(value) != value { + return nil, errors.New("URL must be HTTPS without credentials, query, or fragment") + } + return u, nil +} + +func validateBootstrapAsset(name string, asset BootstrapAssetRef) error { + _, urlErr := recoveryHTTPSURL(asset.URL) + if urlErr != nil { + urlErr = fmt.Errorf("%s.url must be HTTPS without credentials, query, or fragment", name) + } + return errors.Join(urlErr, errors.Join(validateRequiredSHA256Digest(name+".digest", asset.Digest)...)) +} + +// Domain-separated JSON is used only by lifecycle envelopes. Bootstrap signs +// its canonical manifest itself; install receipts sign their protobuf payload. +func recoverySigningJSON(domain string, payload any) ([]byte, error) { + return json.Marshal(struct { + Domain string `json:"domain"` + Payload any `json:"payload"` + }{Domain: domain, Payload: payload}) +} diff --git a/protocol/bootstrap_test.go b/protocol/bootstrap_test.go new file mode 100644 index 0000000..a845540 --- /dev/null +++ b/protocol/bootstrap_test.go @@ -0,0 +1,269 @@ +package protocol_test + +import ( + "bytes" + "crypto/ed25519" + "encoding/base64" + "encoding/json" + "strings" + "testing" + "time" + + "github.com/GoCodeAlone/workflow-plugin-compute-core/protocol" + "google.golang.org/protobuf/encoding/protowire" + "google.golang.org/protobuf/proto" +) + +var recoveryNow = time.Date(2026, 10, 3, 12, 0, 0, 0, time.UTC) + +func recoveryKey() ed25519.PrivateKey { + return ed25519.NewKeyFromSeed(bytes.Repeat([]byte{7}, ed25519.SeedSize)) +} + +func recoveryDigest() string { return "sha256:" + strings.Repeat("a", 64) } + +func bootstrapFixture() protocol.ComputeBootstrapManifest { + public := recoveryKey().Public().(ed25519.PublicKey) + asset := protocol.BootstrapAssetRef{URL: "https://releases.example.test/immutable/artifact", Digest: recoveryDigest()} + return protocol.ComputeBootstrapManifest{ + ProtocolVersion: protocol.Version, SchemaVersion: protocol.ComputeBootstrapManifestSchema, + Version: "v1.0.0", ServerOrigin: "https://compute.example.test", + WFCTLVersion: "v0.86.0", WFCTLAssets: []protocol.BootstrapPlatformAsset{{OS: "linux", Arch: "amd64", Asset: asset}}, + PluginComputeVersion: "v1.0.0", PluginComputeArchive: asset, Bootstrap: asset, + PackageID: "agent-core-linux-amd64", PackageVersion: "v1.0.0", PackageManifestDigest: recoveryDigest(), + BootstrapEpoch: 1, PackageEpoch: 1, KeysetEpoch: 1, KeysetDigest: recoveryDigest(), + PackageSigningKeys: []protocol.BootstrapSigningKey{{KeyID: protocol.Ed25519KeyID(public), PublicKey: base64.StdEncoding.EncodeToString(public)}}, + ExpiresAt: recoveryNow.Add(time.Hour), + } +} + +func installReceiptFixture() protocol.AgentSetupInstallReceipt { + return protocol.AgentSetupInstallReceipt{ + ProtocolVersion: protocol.Version, ReceiptID: "receipt-1", Challenge: base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{9}, 32)), + InviteID: "invite-1", InstallSessionID: "session-1", WorkerID: "worker-1", + PackageID: "agent-core-linux-amd64", PackageVersion: "v1.0.0", PackageDigest: recoveryDigest(), ManifestDigest: recoveryDigest(), + BootstrapEpoch: 1, PackageEpoch: 1, KeysetEpoch: 1, + LauncherDigest: recoveryDigest(), SlotID: "A", SlotDigest: recoveryDigest(), ServiceUnitDigest: recoveryDigest(), ServiceMode: "systemd-user", StartedPID: 123, + HeartbeatID: "heartbeat-1", HeartbeatAt: recoveryNow.Add(-time.Minute), + CloudFinalUnit: "cloud-final.service", ScrubUnit: "compute-scrub.service", WrapperHash: recoveryDigest(), ScanHitCount: 0, CodeDigest: recoveryDigest(), + CompletedAt: recoveryNow, ExpiresAt: recoveryNow.Add(time.Hour), + } +} + +func TestBootstrapManifest_StrictDecodeAndValidation(t *testing.T) { + for name, mutate := range map[string]func(*protocol.ComputeBootstrapManifest){ + "origin path": func(m *protocol.ComputeBootstrapManifest) { m.ServerOrigin += "/api" }, + "origin userinfo": func(m *protocol.ComputeBootstrapManifest) { m.ServerOrigin = "https://token@compute.example.test" }, + "http asset": func(m *protocol.ComputeBootstrapManifest) { m.Bootstrap.URL = "http://example.test/asset" }, + "asset query": func(m *protocol.ComputeBootstrapManifest) { m.Bootstrap.URL += "?token=known-contract-private-value" }, + "digest": func(m *protocol.ComputeBootstrapManifest) { m.PackageManifestDigest = "sha256:bad" }, + "zero epoch": func(m *protocol.ComputeBootstrapManifest) { m.PackageEpoch = 0 }, + "wrong key ID": func(m *protocol.ComputeBootstrapManifest) { m.PackageSigningKeys[0].KeyID = recoveryDigest() }, + "duplicate platform": func(m *protocol.ComputeBootstrapManifest) { m.WFCTLAssets = append(m.WFCTLAssets, m.WFCTLAssets[0]) }, + "expired": func(m *protocol.ComputeBootstrapManifest) { m.ExpiresAt = recoveryNow }, + } { + t.Run(name, func(t *testing.T) { + m := bootstrapFixture() + mutate(&m) + if err := m.ValidateAt(recoveryNow); err == nil || strings.Contains(err.Error(), "known-contract-private-value") { + t.Fatalf("unsafe manifest accepted or disclosed input: %v", err) + } + }) + } + m := bootstrapFixture() + data, err := json.Marshal(m) + if err != nil { + t.Fatal(err) + } + for _, data := range [][]byte{ + append(append([]byte(nil), data...), []byte(` {}`)...), + bytes.Replace(data, []byte(`"version":"v1.0.0"`), []byte(`"version":"v1.0.0","unknown":true`), 1), + bytes.Replace(data, []byte(`"arch":"amd64"`), []byte(`"arch":"amd64","env":{}`), 1), + } { + var decoded protocol.ComputeBootstrapManifest + if err := protocol.DecodeStrict(bytes.NewReader(data), &decoded); err == nil { + t.Fatal("strict manifest decode accepted unknown/trailing data") + } + } + if err := m.ValidateAt(time.Time{}); err == nil { + t.Fatal("zero validation clock accepted") + } +} + +func TestBootstrapManifest_CanonicalEd25519Bindings(t *testing.T) { + m := bootstrapFixture() + key := recoveryKey() + public := key.Public().(ed25519.PublicKey) + sig, err := m.Sign(key) + if err != nil { + t.Fatal(err) + } + if err := m.Verify(sig, public, recoveryNow); err != nil { + t.Fatal(err) + } + data, err := m.CanonicalBytes() + if err != nil { + t.Fatal(err) + } + var consumer protocol.ComputeBootstrapManifest + if err := protocol.DecodeStrict(bytes.NewReader(data), &consumer); err != nil { + t.Fatal(err) + } + again, err := consumer.CanonicalBytes() + if err != nil || !bytes.Equal(data, again) { + t.Fatalf("cross-consumer canonical bytes differ: %v", err) + } + consumer.ExpiresAt = consumer.ExpiresAt.In(time.FixedZone("fixture", 3600)) + again, err = consumer.CanonicalBytes() + if err != nil || !bytes.Equal(data, again) { + t.Fatal("canonical bytes depend on timezone") + } + for _, mutate := range []func(*protocol.ComputeBootstrapManifest){ + func(m *protocol.ComputeBootstrapManifest) { m.ServerOrigin = "https://other.example.test" }, + func(m *protocol.ComputeBootstrapManifest) { m.Bootstrap.Digest = "sha256:" + strings.Repeat("b", 64) }, + func(m *protocol.ComputeBootstrapManifest) { m.KeysetEpoch++ }, + func(m *protocol.ComputeBootstrapManifest) { m.PackageID = "other-package" }, + } { + changed := bootstrapFixture() + mutate(&changed) + if err := changed.Verify(sig, public, recoveryNow); err == nil { + t.Fatal("manifest signature accepted substitution") + } + } + wrong := ed25519.NewKeyFromSeed(bytes.Repeat([]byte{8}, ed25519.SeedSize)).Public().(ed25519.PublicKey) + if err := m.Verify(sig, wrong, recoveryNow); err == nil { + t.Fatal("wrong bootstrap root accepted") + } + if _, err := m.Sign(nil); err == nil { + t.Fatal("invalid private key accepted") + } + sig.Value += "\n" + if err := m.Verify(sig, public, recoveryNow); err == nil { + t.Fatal("noncanonical signature encoding accepted") + } +} + +func TestInstallReceipt_BindingReplayAndEd25519(t *testing.T) { + r := installReceiptFixture() + expected := r.Binding() + key := recoveryKey() + public := key.Public().(ed25519.PublicKey) + if err := r.Sign(key); err != nil { + t.Fatal(err) + } + if err := r.Verify(public, expected, recoveryNow); err != nil { + t.Fatal(err) + } + first, err := r.SigningBytes() + if err != nil { + t.Fatal(err) + } + if err := r.Sign(key); err != nil { + t.Fatal(err) + } + second, err := r.SigningBytes() + if err != nil || !bytes.Equal(first, second) { + t.Fatal("identical retry changed receipt bytes") + } + for name, mutate := range map[string]func(*protocol.AgentSetupInstallReceipt){ + "worker": func(r *protocol.AgentSetupInstallReceipt) { r.WorkerID = "other-worker" }, + "session": func(r *protocol.AgentSetupInstallReceipt) { r.InstallSessionID = "other-session" }, + "invite": func(r *protocol.AgentSetupInstallReceipt) { r.InviteID = "other-invite" }, + "challenge": func(r *protocol.AgentSetupInstallReceipt) { + r.Challenge = base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{10}, 32)) + }, + "manifest": func(r *protocol.AgentSetupInstallReceipt) { r.ManifestDigest = "sha256:" + strings.Repeat("b", 64) }, + "package digest": func(r *protocol.AgentSetupInstallReceipt) { r.PackageDigest = "sha256:" + strings.Repeat("b", 64) }, + "package ID": func(r *protocol.AgentSetupInstallReceipt) { r.PackageID = "other-package" }, + "package version": func(r *protocol.AgentSetupInstallReceipt) { r.PackageVersion = "v2.0.0" }, + "receipt ID": func(r *protocol.AgentSetupInstallReceipt) { r.ReceiptID = "other-receipt" }, + "bootstrap epoch": func(r *protocol.AgentSetupInstallReceipt) { r.BootstrapEpoch++ }, + "package epoch": func(r *protocol.AgentSetupInstallReceipt) { r.PackageEpoch++ }, + "keyset": func(r *protocol.AgentSetupInstallReceipt) { r.KeysetEpoch++ }, + "launcher": func(r *protocol.AgentSetupInstallReceipt) { r.LauncherDigest = "sha256:" + strings.Repeat("b", 64) }, + "slot": func(r *protocol.AgentSetupInstallReceipt) { r.SlotID = "B" }, + "slot digest": func(r *protocol.AgentSetupInstallReceipt) { r.SlotDigest = "sha256:" + strings.Repeat("b", 64) }, + "service unit": func(r *protocol.AgentSetupInstallReceipt) { r.ServiceUnitDigest = "sha256:" + strings.Repeat("b", 64) }, + "service mode": func(r *protocol.AgentSetupInstallReceipt) { r.ServiceMode = "other-mode" }, + "started PID": func(r *protocol.AgentSetupInstallReceipt) { r.StartedPID++ }, + "heartbeat ID": func(r *protocol.AgentSetupInstallReceipt) { r.HeartbeatID = "other-heartbeat" }, + "heartbeat time": func(r *protocol.AgentSetupInstallReceipt) { r.HeartbeatAt = r.HeartbeatAt.Add(-time.Second) }, + "cloud final": func(r *protocol.AgentSetupInstallReceipt) { r.CloudFinalUnit = "other-cloud-final.service" }, + "scrub unit": func(r *protocol.AgentSetupInstallReceipt) { r.ScrubUnit = "other-scrub.service" }, + "wrapper": func(r *protocol.AgentSetupInstallReceipt) { r.WrapperHash = "sha256:" + strings.Repeat("b", 64) }, + "code": func(r *protocol.AgentSetupInstallReceipt) { r.CodeDigest = "sha256:" + strings.Repeat("b", 64) }, + "completed time": func(r *protocol.AgentSetupInstallReceipt) { r.CompletedAt = r.CompletedAt.Add(-time.Second) }, + "expiry": func(r *protocol.AgentSetupInstallReceipt) { r.ExpiresAt = r.ExpiresAt.Add(time.Second) }, + "scrub": func(r *protocol.AgentSetupInstallReceipt) { r.ScanHitCount = 1 }, + } { + t.Run(name, func(t *testing.T) { + changed := r + mutate(&changed) + if err := changed.Verify(public, expected, recoveryNow); err == nil { + t.Fatal("signed receipt accepted changed payload") + } + if err := changed.Verify(public, changed.Binding(), recoveryNow); err == nil { + t.Fatal("signature omitted a receipt payload field") + } + if name == "worker" || name == "challenge" || name == "session" || name == "invite" || name == "manifest" || name == "keyset" { + if err := changed.Sign(key); err != nil { + t.Fatal(err) + } + if err := changed.Verify(public, expected, recoveryNow); err == nil { + t.Fatal("valid signature accepted different stored setup binding") + } + } + }) + } + if err := r.Verify(public, expected, r.ExpiresAt); err == nil { + t.Fatal("expired receipt accepted") + } + wrong := ed25519.NewKeyFromSeed(bytes.Repeat([]byte{8}, ed25519.SeedSize)).Public().(ed25519.PublicKey) + if err := r.Verify(wrong, expected, recoveryNow); err == nil { + t.Fatal("wrong receipt key accepted") + } + r.Signature.Value = "forged" + r.Signature.Verified = true + if err := r.Verify(public, expected, recoveryNow); err == nil { + t.Fatal("caller verified flag authorized forged receipt") + } +} + +func TestInstallReceipt_DeterministicProtoAndStrictJSON(t *testing.T) { + r := installReceiptFixture() + payload := r.SigningPayload() + data, err := proto.MarshalOptions{Deterministic: true}.Marshal(payload) + if err != nil { + t.Fatal(err) + } + want, err := r.SigningBytes() + if err != nil || !bytes.Equal(data, want) { + t.Fatalf("receipt does not sign the separate protobuf payload: %v", err) + } + decoded, err := protocol.UnmarshalAgentSetupInstallReceiptPayloadStrict(data) + if err != nil || !proto.Equal(payload, decoded) { + t.Fatalf("strict receipt protobuf roundtrip: %v", err) + } + data = append(data, protowire.AppendTag(nil, 99, protowire.VarintType)...) + data = protowire.AppendVarint(data, 1) + if _, err := protocol.UnmarshalAgentSetupInstallReceiptPayloadStrict(data); err == nil { + t.Fatal("unknown receipt protobuf field accepted") + } + payload.Domain = "managed-provider-lifecycle-request.v1" + wrongDomain, err := proto.Marshal(payload) + if err != nil { + t.Fatal(err) + } + if _, err := protocol.UnmarshalAgentSetupInstallReceiptPayloadStrict(wrongDomain); err == nil { + t.Fatal("cross-domain receipt payload accepted") + } + jsonData, err := json.Marshal(r) + if err != nil { + t.Fatal(err) + } + jsonData = bytes.Replace(jsonData, []byte(`"receipt_id":"receipt-1"`), []byte(`"receipt_id":"receipt-1","token":"known-contract-private-value"`), 1) + var got protocol.AgentSetupInstallReceipt + if err := protocol.DecodeStrict(bytes.NewReader(jsonData), &got); err == nil || strings.Contains(err.Error(), "known-contract-private-value") { + t.Fatalf("unsafe receipt JSON accepted/disclosed: %v", err) + } +} diff --git a/protocol/managed_lifecycle.go b/protocol/managed_lifecycle.go new file mode 100644 index 0000000..7ecc2f1 --- /dev/null +++ b/protocol/managed_lifecycle.go @@ -0,0 +1,391 @@ +package protocol + +import ( + "crypto/ed25519" + "crypto/sha256" + "encoding/hex" + "errors" + "net/url" + "strings" + "time" +) + +const ( + ManagedProviderLifecycleCapability = "managed-provider-lifecycle-v1" + ManagedProviderLifecycleCommand = "managed-provider-lifecycle" + DedicatedProviderHostPreflightVersion = "dedicated-provider-host-preflight.v1" +) + +type ManagedLifecycleAction string + +const ( + ManagedLifecyclePrepare ManagedLifecycleAction = "prepare" + ManagedLifecycleActivate ManagedLifecycleAction = "activate" + ManagedLifecycleDeactivate ManagedLifecycleAction = "deactivate" + ManagedLifecycleRollback ManagedLifecycleAction = "rollback" + ManagedLifecycleStatus ManagedLifecycleAction = "status" +) + +func validManagedLifecycleAction(action ManagedLifecycleAction) bool { + switch action { + case ManagedLifecyclePrepare, ManagedLifecycleActivate, ManagedLifecycleDeactivate, ManagedLifecycleRollback, ManagedLifecycleStatus: + return true + default: + return false + } +} + +// ManagedLifecycleArtifactRef describes encrypted, server-relative material. +// Content fetch/ack, key custody, and terminal revocation remain host-owned. +type ManagedLifecycleArtifactRef struct { + ProtocolVersion string `json:"protocol_version"` + TransactionID string `json:"transaction_id"` + WorkerID string `json:"worker_id"` + PluginID string `json:"plugin_id"` + Component string `json:"component"` + ComponentDigest string `json:"component_digest"` + Ref string `json:"ref"` + Digest string `json:"digest"` + MaxBytes int64 `json:"max_bytes"` + EnvelopeKeyID string `json:"envelope_key_id"` + ExpiresAt time.Time `json:"expires_at"` + Signature SignatureEnvelope `json:"signature,omitzero"` +} + +func (r ManagedLifecycleArtifactRef) validate() error { + var errs []error + errs = append(errs, recoveryProtocolError(r.ProtocolVersion)) + for _, field := range []struct{ name, value string }{ + {"transaction_id", r.TransactionID}, {"worker_id", r.WorkerID}, {"plugin_id", r.PluginID}, {"component", r.Component}, {"envelope_key_id", r.EnvelopeKeyID}, + } { + errs = append(errs, validateIdentifier(field.name, field.value)) + } + errs = append(errs, validateRequiredSHA256Digest("component_digest", r.ComponentDigest)...) + errs = append(errs, validateRequiredSHA256Digest("digest", r.Digest)...) + u, err := url.Parse(r.Ref) + if err != nil || u.IsAbs() || u.Host != "" || u.User != nil || u.RawQuery != "" || u.ForceQuery || u.Fragment != "" || !strings.HasPrefix(r.Ref, "/") || strings.HasPrefix(r.Ref, "//") || strings.ContainsAny(r.Ref, "\\\x00\r\n") || strings.ContainsAny(u.Path, "\\%\x00\r\n") || strings.HasPrefix(u.Path, "//") { + errs = append(errs, errors.New("ref must be a server-relative artifact URL without query, fragment, or credentials")) + } else { + for _, segment := range strings.Split(u.Path, "/") { + if segment == ".." || segment == "." { + errs = append(errs, errors.New("ref must not contain traversal")) + break + } + } + } + if r.MaxBytes <= 0 { + errs = append(errs, errors.New("max_bytes must be positive")) + } + if r.ExpiresAt.IsZero() { + errs = append(errs, errors.New("expires_at is required")) + } + return errors.Join(errs...) +} + +func (r ManagedLifecycleArtifactRef) ValidateAt(now time.Time) error { + return errors.Join(r.validate(), recoveryExpiryError(r.ExpiresAt, now)) +} + +func (r ManagedLifecycleArtifactRef) SigningBytes() ([]byte, error) { + if err := r.validate(); err != nil { + return nil, err + } + r.Signature = SignatureEnvelope{} + r.ExpiresAt = r.ExpiresAt.UTC() + return recoverySigningJSON("managed-lifecycle-artifact.v1", r) +} + +func (r *ManagedLifecycleArtifactRef) Sign(key ed25519.PrivateKey) error { + data, err := r.SigningBytes() + if err != nil { + return err + } + r.Signature, err = signRecoveryBytes(data, key) + return err +} + +func (r ManagedLifecycleArtifactRef) Verify(public ed25519.PublicKey, now time.Time) error { + if err := r.ValidateAt(now); err != nil { + return err + } + data, err := r.SigningBytes() + if err != nil { + return err + } + return verifyRecoveryBytes(data, r.Signature, public) +} + +// The command and executable are deliberately absent. Consumers resolve only +// the promoted component digest, then pass this request on the fixed stdin path. +type ManagedProviderLifecycleRequest struct { + ProtocolVersion string `json:"protocol_version"` + Action ManagedLifecycleAction `json:"action"` + RequiredCapability string `json:"required_capability"` + TransactionID string `json:"transaction_id"` + WorkerID string `json:"worker_id"` + PluginID string `json:"plugin_id"` + Component string `json:"component"` + Version string `json:"version"` + Digest string `json:"digest"` + ConfigRef ManagedLifecycleArtifactRef `json:"config_ref"` + ConfigHash string `json:"config_hash"` + SecretRefs []ManagedLifecycleArtifactRef `json:"secret_refs"` + PriorStateHash string `json:"prior_state_hash"` + ExpiresAt time.Time `json:"expires_at"` + Signature SignatureEnvelope `json:"signature,omitzero"` +} + +func (r ManagedProviderLifecycleRequest) validate() error { + var errs []error + errs = append(errs, recoveryProtocolError(r.ProtocolVersion)) + if !validManagedLifecycleAction(r.Action) { + errs = append(errs, errors.New("action must be prepare, activate, deactivate, rollback, or status")) + } + if r.RequiredCapability != ManagedProviderLifecycleCapability { + errs = append(errs, errors.New("required_capability must be managed-provider-lifecycle-v1")) + } + for _, field := range []struct{ name, value string }{ + {"transaction_id", r.TransactionID}, {"worker_id", r.WorkerID}, {"plugin_id", r.PluginID}, {"component", r.Component}, {"version", r.Version}, + } { + errs = append(errs, validateIdentifier(field.name, field.value)) + } + for _, field := range []struct{ name, value string }{ + {"digest", r.Digest}, {"config_hash", r.ConfigHash}, {"prior_state_hash", r.PriorStateHash}, + } { + errs = append(errs, validateRequiredSHA256Digest(field.name, field.value)...) + } + if r.ConfigHash != r.ConfigRef.Digest { + errs = append(errs, errors.New("config_hash must match config_ref.digest")) + } + refs := append([]ManagedLifecycleArtifactRef{r.ConfigRef}, r.SecretRefs...) + seen := make(map[string]bool) + for _, ref := range refs { + errs = append(errs, ref.validate()) + if ref.TransactionID != r.TransactionID || ref.WorkerID != r.WorkerID || ref.PluginID != r.PluginID || ref.Component != r.Component || ref.ComponentDigest != r.Digest { + errs = append(errs, errors.New("artifact ref must match request transaction, worker, plugin, component, and promoted digest")) + } + if seen[ref.Ref] { + errs = append(errs, errors.New("artifact refs must not repeat a server-relative URL")) + } + seen[ref.Ref] = true + } + if r.ExpiresAt.IsZero() { + errs = append(errs, errors.New("expires_at is required")) + } + return errors.Join(errs...) +} + +func (r ManagedProviderLifecycleRequest) ValidateAt(now time.Time) error { + var errs []error + errs = append(errs, r.validate(), recoveryExpiryError(r.ExpiresAt, now), r.ConfigRef.ValidateAt(now)) + for _, ref := range r.SecretRefs { + errs = append(errs, ref.ValidateAt(now)) + } + return errors.Join(errs...) +} + +func (r ManagedProviderLifecycleRequest) SigningBytes() ([]byte, error) { + if err := r.validate(); err != nil { + return nil, err + } + r.Signature = SignatureEnvelope{} + r.ExpiresAt = r.ExpiresAt.UTC() + r.ConfigRef.ExpiresAt = r.ConfigRef.ExpiresAt.UTC() + r.SecretRefs = append([]ManagedLifecycleArtifactRef{}, r.SecretRefs...) + for i := range r.SecretRefs { + r.SecretRefs[i].ExpiresAt = r.SecretRefs[i].ExpiresAt.UTC() + } + return recoverySigningJSON("managed-provider-lifecycle-request.v1", r) +} + +func (r *ManagedProviderLifecycleRequest) Sign(key ed25519.PrivateKey) error { + data, err := r.SigningBytes() + if err != nil { + return err + } + r.Signature, err = signRecoveryBytes(data, key) + return err +} + +// BindingHash binds a terminal result to every signature-free request input. +func (r ManagedProviderLifecycleRequest) BindingHash() (string, error) { + data, err := r.SigningBytes() + if err != nil { + return "", err + } + sum := sha256.Sum256(data) + return "sha256:" + hex.EncodeToString(sum[:]), nil +} + +func (r ManagedProviderLifecycleRequest) Verify(public ed25519.PublicKey, now time.Time) error { + if err := r.ValidateAt(now); err != nil { + return err + } + data, err := r.SigningBytes() + if err != nil { + return err + } + return verifyRecoveryBytes(data, r.Signature, public) +} + +type ManagedLifecycleResultStatus string +type ManagedLifecycleRollbackResult string + +const ( + ManagedLifecycleSucceeded ManagedLifecycleResultStatus = "succeeded" + ManagedLifecycleFailed ManagedLifecycleResultStatus = "failed" + ManagedLifecycleRollbackNotRequired ManagedLifecycleRollbackResult = "not-required" + ManagedLifecycleRollbackSucceeded ManagedLifecycleRollbackResult = "succeeded" + ManagedLifecycleRollbackFailed ManagedLifecycleRollbackResult = "failed" +) + +type ManagedProviderLifecycleResult struct { + ProtocolVersion string `json:"protocol_version"` + TransactionID string `json:"transaction_id"` + WorkerID string `json:"worker_id"` + PluginID string `json:"plugin_id"` + Component string `json:"component"` + Version string `json:"version"` + Digest string `json:"digest"` + Action ManagedLifecycleAction `json:"action"` + RequestHash string `json:"request_hash"` + IdempotencyState string `json:"idempotency_state"` + ServiceHash string `json:"service_hash"` + StateHash string `json:"state_hash"` + Status ManagedLifecycleResultStatus `json:"status"` + RollbackResult ManagedLifecycleRollbackResult `json:"rollback_result"` + CompletedAt time.Time `json:"completed_at"` + ExpiresAt time.Time `json:"expires_at"` + Signature SignatureEnvelope `json:"signature,omitzero"` +} + +func (r ManagedProviderLifecycleResult) validate() error { + var errs []error + errs = append(errs, recoveryProtocolError(r.ProtocolVersion)) + for _, field := range []struct{ name, value string }{ + {"transaction_id", r.TransactionID}, {"worker_id", r.WorkerID}, {"plugin_id", r.PluginID}, {"component", r.Component}, {"version", r.Version}, {"idempotency_state", r.IdempotencyState}, + } { + errs = append(errs, validateIdentifier(field.name, field.value)) + errs = append(errs, validateRuntimeBackendPublicText(field.name, field.value)...) + } + for _, field := range []struct{ name, value string }{{"digest", r.Digest}, {"request_hash", r.RequestHash}, {"service_hash", r.ServiceHash}, {"state_hash", r.StateHash}} { + errs = append(errs, validateRequiredSHA256Digest(field.name, field.value)...) + } + if !validManagedLifecycleAction(r.Action) || (r.Status != ManagedLifecycleSucceeded && r.Status != ManagedLifecycleFailed) { + errs = append(errs, errors.New("action or status is not supported")) + } + if r.RollbackResult != ManagedLifecycleRollbackNotRequired && r.RollbackResult != ManagedLifecycleRollbackSucceeded && r.RollbackResult != ManagedLifecycleRollbackFailed { + errs = append(errs, errors.New("rollback_result is not supported")) + } + if r.CompletedAt.IsZero() || r.ExpiresAt.IsZero() || !r.CompletedAt.Before(r.ExpiresAt) { + errs = append(errs, errors.New("completed_at must be nonzero and precede expires_at")) + } + return errors.Join(errs...) +} + +func (r ManagedProviderLifecycleResult) ValidateAt(now time.Time) error { + var future error + if r.CompletedAt.After(now) { + future = errors.New("completed_at must not be in the future") + } + return errors.Join(r.validate(), recoveryExpiryError(r.ExpiresAt, now), future) +} + +func (r ManagedProviderLifecycleResult) SigningBytes() ([]byte, error) { + if err := r.validate(); err != nil { + return nil, err + } + r.Signature = SignatureEnvelope{} + r.CompletedAt, r.ExpiresAt = r.CompletedAt.UTC(), r.ExpiresAt.UTC() + return recoverySigningJSON("managed-provider-lifecycle-result.v1", r) +} + +func (r *ManagedProviderLifecycleResult) Sign(key ed25519.PrivateKey) error { + data, err := r.SigningBytes() + if err != nil { + return err + } + r.Signature, err = signRecoveryBytes(data, key) + return err +} + +func (r ManagedProviderLifecycleResult) Verify(public ed25519.PublicKey, request ManagedProviderLifecycleRequest, now time.Time) error { + if err := r.ValidateAt(now); err != nil { + return err + } + if err := request.ValidateAt(now); err != nil { + return err + } + requestHash, err := request.BindingHash() + if err != nil { + return err + } + if r.RequestHash != requestHash || r.TransactionID != request.TransactionID || r.WorkerID != request.WorkerID || r.PluginID != request.PluginID || r.Component != request.Component || r.Version != request.Version || r.Digest != request.Digest || r.Action != request.Action || !r.ExpiresAt.Equal(request.ExpiresAt) { + return errors.New("lifecycle result does not match the request binding") + } + data, err := r.SigningBytes() + if err != nil { + return err + } + return verifyRecoveryBytes(data, r.Signature, public) +} + +type HostServiceState string + +const ( + HostServiceActive HostServiceState = "active" + HostServiceInactive HostServiceState = "inactive" + HostServiceFailed HostServiceState = "failed" + HostServiceActivating HostServiceState = "activating" + HostServiceDeactivating HostServiceState = "deactivating" + HostServiceNotFound HostServiceState = "not-found" +) + +// Evidence contains only public identity, hashes, presence-aware booleans and +// fixed service states. No ambient process, file, or environment bytes cross it. +type DedicatedProviderHostPreflightResult struct { + ProtocolVersion string `json:"protocol_version"` + WorkerID string `json:"worker_id"` + OS string `json:"os"` + UID uint32 `json:"uid"` + HomeHash string `json:"home_hash"` + MachineIDHash string `json:"machine_id_hash"` + AgentUnitDigest string `json:"agent_unit_digest"` + LauncherUnitDigest string `json:"launcher_unit_digest"` + FirewallPolicyHash string `json:"firewall_policy_hash"` + AgentServiceState HostServiceState `json:"agent_service_state"` + LauncherServiceState HostServiceState `json:"launcher_service_state"` + UserBusAvailable *bool `json:"user_bus_available"` + LingeringEnabled *bool `json:"lingering_enabled"` + RootlessPodman *bool `json:"rootless_podman"` + ActionsRunnerProcessPresent *bool `json:"actions_runner_process_present"` + ActionsRunnerServicePresent *bool `json:"actions_runner_service_present"` +} + +func (r DedicatedProviderHostPreflightResult) Validate() error { + var errs []error + if r.ProtocolVersion != DedicatedProviderHostPreflightVersion || r.OS != "linux" { + errs = append(errs, errors.New("protocol_version and os must identify the Linux dedicated-host preflight")) + } + errs = append(errs, validateIdentifier("worker_id", r.WorkerID)) + errs = append(errs, validateRuntimeBackendPublicText("worker_id", r.WorkerID)...) + for _, field := range []struct{ name, value string }{ + {"home_hash", r.HomeHash}, {"machine_id_hash", r.MachineIDHash}, {"agent_unit_digest", r.AgentUnitDigest}, {"launcher_unit_digest", r.LauncherUnitDigest}, {"firewall_policy_hash", r.FirewallPolicyHash}, + } { + errs = append(errs, validateRequiredSHA256Digest(field.name, field.value)...) + } + for _, state := range []HostServiceState{r.AgentServiceState, r.LauncherServiceState} { + switch state { + case HostServiceActive, HostServiceInactive, HostServiceFailed, HostServiceActivating, HostServiceDeactivating, HostServiceNotFound: + default: + errs = append(errs, errors.New("service state is not supported")) + } + } + if r.UserBusAvailable == nil || r.LingeringEnabled == nil || r.RootlessPodman == nil || r.ActionsRunnerProcessPresent == nil || r.ActionsRunnerServicePresent == nil { + errs = append(errs, errors.New("preflight booleans must be explicitly present")) + } + if (r.ActionsRunnerProcessPresent != nil && *r.ActionsRunnerProcessPresent) || (r.ActionsRunnerServicePresent != nil && *r.ActionsRunnerServicePresent) { + errs = append(errs, errors.New("dedicated host must contain no Actions runner process or service")) + } + return errors.Join(errs...) +} diff --git a/protocol/managed_lifecycle_test.go b/protocol/managed_lifecycle_test.go new file mode 100644 index 0000000..3b578fc --- /dev/null +++ b/protocol/managed_lifecycle_test.go @@ -0,0 +1,231 @@ +package protocol_test + +import ( + "bytes" + "crypto/ed25519" + "encoding/json" + "strings" + "testing" + "time" + + "github.com/GoCodeAlone/workflow-plugin-compute-core/protocol" +) + +func lifecycleFixture(t *testing.T) protocol.ManagedProviderLifecycleRequest { + t.Helper() + ref := protocol.ManagedLifecycleArtifactRef{ + ProtocolVersion: protocol.Version, TransactionID: "transaction-1", WorkerID: "worker-1", PluginID: "workflow-plugin-example", Component: "provider-binary", ComponentDigest: recoveryDigest(), + Ref: "/api/artifacts/config-1", Digest: recoveryDigest(), MaxBytes: 4096, EnvelopeKeyID: "lifecycle-key-1", ExpiresAt: recoveryNow.Add(time.Hour), + } + if err := ref.Sign(recoveryKey()); err != nil { + t.Fatal(err) + } + return protocol.ManagedProviderLifecycleRequest{ + ProtocolVersion: protocol.Version, Action: protocol.ManagedLifecyclePrepare, RequiredCapability: protocol.ManagedProviderLifecycleCapability, + TransactionID: ref.TransactionID, WorkerID: ref.WorkerID, PluginID: ref.PluginID, Component: ref.Component, Version: "v1.0.0", Digest: ref.ComponentDigest, + ConfigRef: ref, ConfigHash: ref.Digest, SecretRefs: []protocol.ManagedLifecycleArtifactRef{}, PriorStateHash: recoveryDigest(), ExpiresAt: ref.ExpiresAt, + } +} + +func TestManagedProviderLifecycle_FixedActionAndExactRefs(t *testing.T) { + if protocol.ManagedProviderLifecycleCommand != "managed-provider-lifecycle" || protocol.ManagedProviderLifecycleCapability != "managed-provider-lifecycle-v1" { + t.Fatal("fixed lifecycle command/capability drifted") + } + for _, action := range []protocol.ManagedLifecycleAction{protocol.ManagedLifecyclePrepare, protocol.ManagedLifecycleActivate, protocol.ManagedLifecycleDeactivate, protocol.ManagedLifecycleRollback, protocol.ManagedLifecycleStatus} { + r := lifecycleFixture(t) + r.Action = action + if err := r.ValidateAt(recoveryNow); err != nil { + t.Fatalf("fixed action %s invalid: %v", action, err) + } + } + for name, mutate := range map[string]func(*protocol.ManagedProviderLifecycleRequest){ + "action": func(r *protocol.ManagedProviderLifecycleRequest) { r.Action = "shell" }, + "capability": func(r *protocol.ManagedProviderLifecycleRequest) { r.RequiredCapability = "other-v1" }, + "cross worker": func(r *protocol.ManagedProviderLifecycleRequest) { r.ConfigRef.WorkerID = "other-worker" }, + "cross transaction": func(r *protocol.ManagedProviderLifecycleRequest) { r.ConfigRef.TransactionID = "other-transaction" }, + "cross component": func(r *protocol.ManagedProviderLifecycleRequest) { r.ConfigRef.Component = "other-component" }, + "cross plugin": func(r *protocol.ManagedProviderLifecycleRequest) { r.ConfigRef.PluginID = "other-plugin" }, + "wrong promoted digest": func(r *protocol.ManagedProviderLifecycleRequest) { + r.ConfigRef.ComponentDigest = "sha256:" + strings.Repeat("b", 64) + }, + "config hash": func(r *protocol.ManagedProviderLifecycleRequest) { r.ConfigHash = "sha256:" + strings.Repeat("b", 64) }, + "absolute ref": func(r *protocol.ManagedProviderLifecycleRequest) { + r.ConfigRef.Ref = "https://other.example.test/artifact" + }, + "traversal ref": func(r *protocol.ManagedProviderLifecycleRequest) { r.ConfigRef.Ref = "/api/%2e%2e/private" }, + "encoded separator": func(r *protocol.ManagedProviderLifecycleRequest) { r.ConfigRef.Ref = "/api/%5cprivate" }, + "nested escaping": func(r *protocol.ManagedProviderLifecycleRequest) { r.ConfigRef.Ref = "/api/%252e%252e/private" }, + "unbounded ref": func(r *protocol.ManagedProviderLifecycleRequest) { r.ConfigRef.MaxBytes = 0 }, + "expired artifact": func(r *protocol.ManagedProviderLifecycleRequest) { r.ConfigRef.ExpiresAt = recoveryNow }, + "expired": func(r *protocol.ManagedProviderLifecycleRequest) { r.ExpiresAt = recoveryNow }, + } { + t.Run(name, func(t *testing.T) { + r := lifecycleFixture(t) + mutate(&r) + if err := r.ValidateAt(recoveryNow); err == nil { + t.Fatal("unsafe lifecycle request accepted") + } + }) + } + r := lifecycleFixture(t) + data, err := json.Marshal(r) + if err != nil { + t.Fatal(err) + } + for _, field := range []string{"binary", "argv", "path", "env", "service_name"} { + injected := append([]byte(`{"`+field+`":"known-contract-private-value",`), data[1:]...) + var decoded protocol.ManagedProviderLifecycleRequest + if err := protocol.DecodeStrict(bytes.NewReader(injected), &decoded); err == nil || strings.Contains(err.Error(), "known-contract-private-value") { + t.Fatalf("forbidden %s accepted/disclosed: %v", field, err) + } + } +} + +func TestManagedProviderLifecycle_SignaturesResultBindingAndRetry(t *testing.T) { + r := lifecycleFixture(t) + key := recoveryKey() + public := key.Public().(ed25519.PublicKey) + if err := r.Sign(key); err != nil { + t.Fatal(err) + } + if err := r.Verify(public, recoveryNow); err != nil { + t.Fatal(err) + } + data, err := json.Marshal(r) + if err != nil { + t.Fatal(err) + } + var consumer protocol.ManagedProviderLifecycleRequest + if err := protocol.DecodeStrict(bytes.NewReader(data), &consumer); err != nil { + t.Fatal(err) + } + if err := consumer.Verify(public, recoveryNow); err != nil { + t.Fatal(err) + } + before, err := consumer.SigningBytes() + if err != nil { + t.Fatal(err) + } + if err := consumer.Sign(key); err != nil { + t.Fatal(err) + } + after, err := consumer.SigningBytes() + if err != nil || !bytes.Equal(before, after) { + t.Fatal("identical lifecycle retry changed signing bytes") + } + consumer.TransactionID = "replayed-transaction" + if err := consumer.Verify(public, recoveryNow); err == nil { + t.Fatal("changed transaction accepted") + } + requestHash, err := r.BindingHash() + if err != nil { + t.Fatal(err) + } + result := protocol.ManagedProviderLifecycleResult{ + ProtocolVersion: protocol.Version, TransactionID: r.TransactionID, WorkerID: r.WorkerID, PluginID: r.PluginID, Component: r.Component, Version: r.Version, Digest: r.Digest, Action: r.Action, + RequestHash: requestHash, + IdempotencyState: "prepared", ServiceHash: recoveryDigest(), StateHash: recoveryDigest(), Status: protocol.ManagedLifecycleSucceeded, + RollbackResult: protocol.ManagedLifecycleRollbackNotRequired, CompletedAt: recoveryNow, ExpiresAt: r.ExpiresAt, + } + if err := result.Sign(key); err != nil { + t.Fatal(err) + } + if err := result.Verify(public, r, recoveryNow); err != nil { + t.Fatal(err) + } + changedSignature := result + changedSignature.Signature = r.Signature + if err := changedSignature.Verify(public, r, recoveryNow); err == nil { + t.Fatal("cross-domain request signature authorized a result") + } + other := r + other.WorkerID = "other-worker" + if err := result.Verify(public, other, recoveryNow); err == nil { + t.Fatal("cross-worker result accepted") + } + other = r + other.PriorStateHash = "sha256:" + strings.Repeat("b", 64) + if err := result.Verify(public, other, recoveryNow); err == nil { + t.Fatal("result replayed against a changed prior-state hash") + } + if err := r.ConfigRef.Verify(public, recoveryNow); err != nil { + t.Fatal(err) + } + wrong := ed25519.NewKeyFromSeed(bytes.Repeat([]byte{8}, ed25519.SeedSize)).Public().(ed25519.PublicKey) + if err := r.Verify(wrong, recoveryNow); err == nil { + t.Fatal("wrong lifecycle key accepted") + } + if err := r.ConfigRef.Verify(public, r.ConfigRef.ExpiresAt); err == nil { + t.Fatal("expired artifact accepted") + } +} + +func TestManagedProviderLifecycle_CanonicalWithoutInputMutation(t *testing.T) { + r := lifecycleFixture(t) + secret := r.ConfigRef + secret.Ref = "/api/artifacts/secret-1" + secret.ExpiresAt = secret.ExpiresAt.In(time.FixedZone("fixture", 3600)) + if err := secret.Sign(recoveryKey()); err != nil { + t.Fatal(err) + } + r.SecretRefs = []protocol.ManagedLifecycleArtifactRef{secret} + data, err := r.SigningBytes() + if err != nil { + t.Fatal(err) + } + if r.SecretRefs[0].ExpiresAt.Location().String() != "fixture" { + t.Fatal("signing mutated caller-owned secret refs") + } + r.SecretRefs[0].ExpiresAt = r.SecretRefs[0].ExpiresAt.UTC() + again, err := r.SigningBytes() + if err != nil || !bytes.Equal(data, again) { + t.Fatal("lifecycle canonical bytes depend on timezone") + } + r.SecretRefs = append(r.SecretRefs, r.SecretRefs[0]) + if err := r.ValidateAt(recoveryNow); err == nil { + t.Fatal("duplicate artifact authority accepted") + } +} + +func boolRef(v bool) *bool { return &v } + +func preflightFixture() protocol.DedicatedProviderHostPreflightResult { + return protocol.DedicatedProviderHostPreflightResult{ + ProtocolVersion: protocol.DedicatedProviderHostPreflightVersion, WorkerID: "worker-1", OS: "linux", UID: 1000, + HomeHash: recoveryDigest(), MachineIDHash: recoveryDigest(), AgentUnitDigest: recoveryDigest(), LauncherUnitDigest: recoveryDigest(), FirewallPolicyHash: recoveryDigest(), + AgentServiceState: protocol.HostServiceActive, LauncherServiceState: protocol.HostServiceActive, + UserBusAvailable: boolRef(true), LingeringEnabled: boolRef(true), RootlessPodman: boolRef(true), ActionsRunnerProcessPresent: boolRef(false), ActionsRunnerServicePresent: boolRef(false), + } +} + +func TestDedicatedProviderHostPreflight_AllowlistAndRunnerDenial(t *testing.T) { + p := preflightFixture() + if err := p.Validate(); err != nil { + t.Fatal(err) + } + data, err := json.Marshal(p) + if err != nil { + t.Fatal(err) + } + for _, field := range []string{"token", "files", "env", "process_output", "home"} { + injected := append([]byte(`{"`+field+`":"known-contract-private-value",`), data[1:]...) + var decoded protocol.DedicatedProviderHostPreflightResult + if err := protocol.DecodeStrict(bytes.NewReader(injected), &decoded); err == nil || strings.Contains(err.Error(), "known-contract-private-value") { + t.Fatalf("preflight %s accepted/disclosed: %v", field, err) + } + } + for _, mutate := range []func(*protocol.DedicatedProviderHostPreflightResult){ + func(p *protocol.DedicatedProviderHostPreflightResult) { p.ActionsRunnerProcessPresent = boolRef(true) }, + func(p *protocol.DedicatedProviderHostPreflightResult) { p.ActionsRunnerServicePresent = boolRef(true) }, + func(p *protocol.DedicatedProviderHostPreflightResult) { p.ActionsRunnerProcessPresent = nil }, + func(p *protocol.DedicatedProviderHostPreflightResult) { + p.AgentServiceState = "known-contract-private-value" + }, + } { + changed := preflightFixture() + mutate(&changed) + if err := changed.Validate(); err == nil || strings.Contains(err.Error(), "known-contract-private-value") { + t.Fatalf("unsafe preflight accepted/disclosed: %v", err) + } + } +} diff --git a/protocol/pb/bootstrap.pb.go b/protocol/pb/bootstrap.pb.go new file mode 100644 index 0000000..40b9659 --- /dev/null +++ b/protocol/pb/bootstrap.pb.go @@ -0,0 +1,382 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: workflow_plugin_compute_core/protocol/v1/bootstrap.proto + +package pb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Signature-free, domain-bound bytes for the ephemeral install receipt key. +type AgentSetupInstallReceiptPayload struct { + state protoimpl.MessageState `protogen:"open.v1"` + Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` + ProtocolVersion string `protobuf:"bytes,2,opt,name=protocol_version,json=protocolVersion,proto3" json:"protocol_version,omitempty"` + ReceiptId string `protobuf:"bytes,3,opt,name=receipt_id,json=receiptId,proto3" json:"receipt_id,omitempty"` + Challenge string `protobuf:"bytes,4,opt,name=challenge,proto3" json:"challenge,omitempty"` + InviteId string `protobuf:"bytes,5,opt,name=invite_id,json=inviteId,proto3" json:"invite_id,omitempty"` + InstallSessionId string `protobuf:"bytes,6,opt,name=install_session_id,json=installSessionId,proto3" json:"install_session_id,omitempty"` + WorkerId string `protobuf:"bytes,7,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty"` + PackageId string `protobuf:"bytes,8,opt,name=package_id,json=packageId,proto3" json:"package_id,omitempty"` + PackageVersion string `protobuf:"bytes,9,opt,name=package_version,json=packageVersion,proto3" json:"package_version,omitempty"` + PackageDigest string `protobuf:"bytes,10,opt,name=package_digest,json=packageDigest,proto3" json:"package_digest,omitempty"` + ManifestDigest string `protobuf:"bytes,11,opt,name=manifest_digest,json=manifestDigest,proto3" json:"manifest_digest,omitempty"` + BootstrapEpoch uint64 `protobuf:"varint,12,opt,name=bootstrap_epoch,json=bootstrapEpoch,proto3" json:"bootstrap_epoch,omitempty"` + PackageEpoch uint64 `protobuf:"varint,13,opt,name=package_epoch,json=packageEpoch,proto3" json:"package_epoch,omitempty"` + KeysetEpoch uint64 `protobuf:"varint,14,opt,name=keyset_epoch,json=keysetEpoch,proto3" json:"keyset_epoch,omitempty"` + LauncherDigest string `protobuf:"bytes,15,opt,name=launcher_digest,json=launcherDigest,proto3" json:"launcher_digest,omitempty"` + SlotId string `protobuf:"bytes,16,opt,name=slot_id,json=slotId,proto3" json:"slot_id,omitempty"` + SlotDigest string `protobuf:"bytes,17,opt,name=slot_digest,json=slotDigest,proto3" json:"slot_digest,omitempty"` + ServiceUnitDigest string `protobuf:"bytes,18,opt,name=service_unit_digest,json=serviceUnitDigest,proto3" json:"service_unit_digest,omitempty"` + ServiceMode string `protobuf:"bytes,19,opt,name=service_mode,json=serviceMode,proto3" json:"service_mode,omitempty"` + StartedPid int64 `protobuf:"varint,20,opt,name=started_pid,json=startedPid,proto3" json:"started_pid,omitempty"` + HeartbeatId string `protobuf:"bytes,21,opt,name=heartbeat_id,json=heartbeatId,proto3" json:"heartbeat_id,omitempty"` + HeartbeatAtUnixNano int64 `protobuf:"varint,22,opt,name=heartbeat_at_unix_nano,json=heartbeatAtUnixNano,proto3" json:"heartbeat_at_unix_nano,omitempty"` + CloudFinalUnit string `protobuf:"bytes,23,opt,name=cloud_final_unit,json=cloudFinalUnit,proto3" json:"cloud_final_unit,omitempty"` + ScrubUnit string `protobuf:"bytes,24,opt,name=scrub_unit,json=scrubUnit,proto3" json:"scrub_unit,omitempty"` + WrapperHash string `protobuf:"bytes,25,opt,name=wrapper_hash,json=wrapperHash,proto3" json:"wrapper_hash,omitempty"` + ScanHitCount uint64 `protobuf:"varint,26,opt,name=scan_hit_count,json=scanHitCount,proto3" json:"scan_hit_count,omitempty"` + CodeDigest string `protobuf:"bytes,27,opt,name=code_digest,json=codeDigest,proto3" json:"code_digest,omitempty"` + CompletedAtUnixNano int64 `protobuf:"varint,28,opt,name=completed_at_unix_nano,json=completedAtUnixNano,proto3" json:"completed_at_unix_nano,omitempty"` + ExpiresAtUnixNano int64 `protobuf:"varint,29,opt,name=expires_at_unix_nano,json=expiresAtUnixNano,proto3" json:"expires_at_unix_nano,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentSetupInstallReceiptPayload) Reset() { + *x = AgentSetupInstallReceiptPayload{} + mi := &file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentSetupInstallReceiptPayload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentSetupInstallReceiptPayload) ProtoMessage() {} + +func (x *AgentSetupInstallReceiptPayload) ProtoReflect() protoreflect.Message { + mi := &file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentSetupInstallReceiptPayload.ProtoReflect.Descriptor instead. +func (*AgentSetupInstallReceiptPayload) Descriptor() ([]byte, []int) { + return file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_rawDescGZIP(), []int{0} +} + +func (x *AgentSetupInstallReceiptPayload) GetDomain() string { + if x != nil { + return x.Domain + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetProtocolVersion() string { + if x != nil { + return x.ProtocolVersion + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetReceiptId() string { + if x != nil { + return x.ReceiptId + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetChallenge() string { + if x != nil { + return x.Challenge + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetInviteId() string { + if x != nil { + return x.InviteId + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetInstallSessionId() string { + if x != nil { + return x.InstallSessionId + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetWorkerId() string { + if x != nil { + return x.WorkerId + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetPackageId() string { + if x != nil { + return x.PackageId + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetPackageVersion() string { + if x != nil { + return x.PackageVersion + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetPackageDigest() string { + if x != nil { + return x.PackageDigest + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetManifestDigest() string { + if x != nil { + return x.ManifestDigest + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetBootstrapEpoch() uint64 { + if x != nil { + return x.BootstrapEpoch + } + return 0 +} + +func (x *AgentSetupInstallReceiptPayload) GetPackageEpoch() uint64 { + if x != nil { + return x.PackageEpoch + } + return 0 +} + +func (x *AgentSetupInstallReceiptPayload) GetKeysetEpoch() uint64 { + if x != nil { + return x.KeysetEpoch + } + return 0 +} + +func (x *AgentSetupInstallReceiptPayload) GetLauncherDigest() string { + if x != nil { + return x.LauncherDigest + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetSlotId() string { + if x != nil { + return x.SlotId + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetSlotDigest() string { + if x != nil { + return x.SlotDigest + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetServiceUnitDigest() string { + if x != nil { + return x.ServiceUnitDigest + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetServiceMode() string { + if x != nil { + return x.ServiceMode + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetStartedPid() int64 { + if x != nil { + return x.StartedPid + } + return 0 +} + +func (x *AgentSetupInstallReceiptPayload) GetHeartbeatId() string { + if x != nil { + return x.HeartbeatId + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetHeartbeatAtUnixNano() int64 { + if x != nil { + return x.HeartbeatAtUnixNano + } + return 0 +} + +func (x *AgentSetupInstallReceiptPayload) GetCloudFinalUnit() string { + if x != nil { + return x.CloudFinalUnit + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetScrubUnit() string { + if x != nil { + return x.ScrubUnit + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetWrapperHash() string { + if x != nil { + return x.WrapperHash + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetScanHitCount() uint64 { + if x != nil { + return x.ScanHitCount + } + return 0 +} + +func (x *AgentSetupInstallReceiptPayload) GetCodeDigest() string { + if x != nil { + return x.CodeDigest + } + return "" +} + +func (x *AgentSetupInstallReceiptPayload) GetCompletedAtUnixNano() int64 { + if x != nil { + return x.CompletedAtUnixNano + } + return 0 +} + +func (x *AgentSetupInstallReceiptPayload) GetExpiresAtUnixNano() int64 { + if x != nil { + return x.ExpiresAtUnixNano + } + return 0 +} + +var File_workflow_plugin_compute_core_protocol_v1_bootstrap_proto protoreflect.FileDescriptor + +const file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_rawDesc = "" + + "\n" + + "8workflow_plugin_compute_core/protocol/v1/bootstrap.proto\x12(workflow_plugin_compute_core.protocol.v1\"\xda\b\n" + + "\x1fAgentSetupInstallReceiptPayload\x12\x16\n" + + "\x06domain\x18\x01 \x01(\tR\x06domain\x12)\n" + + "\x10protocol_version\x18\x02 \x01(\tR\x0fprotocolVersion\x12\x1d\n" + + "\n" + + "receipt_id\x18\x03 \x01(\tR\treceiptId\x12\x1c\n" + + "\tchallenge\x18\x04 \x01(\tR\tchallenge\x12\x1b\n" + + "\tinvite_id\x18\x05 \x01(\tR\binviteId\x12,\n" + + "\x12install_session_id\x18\x06 \x01(\tR\x10installSessionId\x12\x1b\n" + + "\tworker_id\x18\a \x01(\tR\bworkerId\x12\x1d\n" + + "\n" + + "package_id\x18\b \x01(\tR\tpackageId\x12'\n" + + "\x0fpackage_version\x18\t \x01(\tR\x0epackageVersion\x12%\n" + + "\x0epackage_digest\x18\n" + + " \x01(\tR\rpackageDigest\x12'\n" + + "\x0fmanifest_digest\x18\v \x01(\tR\x0emanifestDigest\x12'\n" + + "\x0fbootstrap_epoch\x18\f \x01(\x04R\x0ebootstrapEpoch\x12#\n" + + "\rpackage_epoch\x18\r \x01(\x04R\fpackageEpoch\x12!\n" + + "\fkeyset_epoch\x18\x0e \x01(\x04R\vkeysetEpoch\x12'\n" + + "\x0flauncher_digest\x18\x0f \x01(\tR\x0elauncherDigest\x12\x17\n" + + "\aslot_id\x18\x10 \x01(\tR\x06slotId\x12\x1f\n" + + "\vslot_digest\x18\x11 \x01(\tR\n" + + "slotDigest\x12.\n" + + "\x13service_unit_digest\x18\x12 \x01(\tR\x11serviceUnitDigest\x12!\n" + + "\fservice_mode\x18\x13 \x01(\tR\vserviceMode\x12\x1f\n" + + "\vstarted_pid\x18\x14 \x01(\x03R\n" + + "startedPid\x12!\n" + + "\fheartbeat_id\x18\x15 \x01(\tR\vheartbeatId\x123\n" + + "\x16heartbeat_at_unix_nano\x18\x16 \x01(\x03R\x13heartbeatAtUnixNano\x12(\n" + + "\x10cloud_final_unit\x18\x17 \x01(\tR\x0ecloudFinalUnit\x12\x1d\n" + + "\n" + + "scrub_unit\x18\x18 \x01(\tR\tscrubUnit\x12!\n" + + "\fwrapper_hash\x18\x19 \x01(\tR\vwrapperHash\x12$\n" + + "\x0escan_hit_count\x18\x1a \x01(\x04R\fscanHitCount\x12\x1f\n" + + "\vcode_digest\x18\x1b \x01(\tR\n" + + "codeDigest\x123\n" + + "\x16completed_at_unix_nano\x18\x1c \x01(\x03R\x13completedAtUnixNano\x12/\n" + + "\x14expires_at_unix_nano\x18\x1d \x01(\x03R\x11expiresAtUnixNanoBDZBgithub.com/GoCodeAlone/workflow-plugin-compute-core/protocol/pb;pbb\x06proto3" + +var ( + file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_rawDescOnce sync.Once + file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_rawDescData []byte +) + +func file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_rawDescGZIP() []byte { + file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_rawDescOnce.Do(func() { + file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_rawDesc), len(file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_rawDesc))) + }) + return file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_rawDescData +} + +var file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_goTypes = []any{ + (*AgentSetupInstallReceiptPayload)(nil), // 0: workflow_plugin_compute_core.protocol.v1.AgentSetupInstallReceiptPayload +} +var file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_init() } +func file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_init() { + if File_workflow_plugin_compute_core_protocol_v1_bootstrap_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_rawDesc), len(file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_goTypes, + DependencyIndexes: file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_depIdxs, + MessageInfos: file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_msgTypes, + }.Build() + File_workflow_plugin_compute_core_protocol_v1_bootstrap_proto = out.File + file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_goTypes = nil + file_workflow_plugin_compute_core_protocol_v1_bootstrap_proto_depIdxs = nil +} diff --git a/protocol/recovery_vectors_test.go b/protocol/recovery_vectors_test.go new file mode 100644 index 0000000..dbe69f5 --- /dev/null +++ b/protocol/recovery_vectors_test.go @@ -0,0 +1,229 @@ +package protocol_test + +import ( + "bytes" + "crypto/ed25519" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "encoding/json" + "flag" + "os" + "strings" + "testing" + "time" + + "github.com/GoCodeAlone/workflow-plugin-compute-core/protocol" +) + +var updateRecoveryVectors = flag.Bool("update-recovery-vectors", false, "regenerate public recovery contract fixtures") + +type recoveryVector[T any] struct { + Message T `json:"message"` + SigningBase64 string `json:"signing_base64"` + SigningDigest string `json:"signing_digest"` +} + +type recoveryRejection[T any] struct { + Name string `json:"name"` + Message T `json:"message"` +} + +type recoveryContractVectors struct { + ValidationTime time.Time `json:"validation_time"` + PublicKey string `json:"public_key"` + WrongPublicKey string `json:"wrong_public_key"` + Manifest recoveryVector[protocol.ComputeBootstrapManifest] `json:"manifest"` + ManifestSignature protocol.SignatureEnvelope `json:"manifest_signature"` + Receipt recoveryVector[protocol.AgentSetupInstallReceipt] `json:"receipt"` + ReceiptBinding protocol.AgentSetupInstallReceiptBinding `json:"receipt_binding"` + Artifact recoveryVector[protocol.ManagedLifecycleArtifactRef] `json:"artifact"` + Request recoveryVector[protocol.ManagedProviderLifecycleRequest] `json:"request"` + Result recoveryVector[protocol.ManagedProviderLifecycleResult] `json:"result"` + Preflight protocol.DedicatedProviderHostPreflightResult `json:"preflight"` + ReceiptRejections []recoveryRejection[protocol.AgentSetupInstallReceipt] `json:"receipt_rejections"` + ChangedRequests []recoveryRejection[protocol.ManagedProviderLifecycleRequest] `json:"changed_requests_for_result"` +} + +func makeRecoveryVector[T any](message T, signing []byte) recoveryVector[T] { + sum := sha256.Sum256(signing) + return recoveryVector[T]{Message: message, SigningBase64: base64.StdEncoding.EncodeToString(signing), SigningDigest: "sha256:" + hex.EncodeToString(sum[:])} +} + +func exportedRecoveryVectors(t *testing.T) recoveryContractVectors { + t.Helper() + key := recoveryKey() + m := bootstrapFixture() + mBytes, err := m.CanonicalBytes() + if err != nil { + t.Fatal(err) + } + mSignature, err := m.Sign(key) + if err != nil { + t.Fatal(err) + } + receipt := installReceiptFixture() + if err := receipt.Sign(key); err != nil { + t.Fatal(err) + } + rBytes, err := receipt.SigningBytes() + if err != nil { + t.Fatal(err) + } + request := lifecycleFixture(t) + if err := request.Sign(key); err != nil { + t.Fatal(err) + } + qBytes, err := request.SigningBytes() + if err != nil { + t.Fatal(err) + } + qHash, err := request.BindingHash() + if err != nil { + t.Fatal(err) + } + result := protocol.ManagedProviderLifecycleResult{ + ProtocolVersion: protocol.Version, TransactionID: request.TransactionID, WorkerID: request.WorkerID, + PluginID: request.PluginID, Component: request.Component, Version: request.Version, Digest: request.Digest, + Action: request.Action, RequestHash: qHash, IdempotencyState: "prepared", ServiceHash: recoveryDigest(), StateHash: recoveryDigest(), + Status: protocol.ManagedLifecycleSucceeded, RollbackResult: protocol.ManagedLifecycleRollbackNotRequired, + CompletedAt: recoveryNow, ExpiresAt: request.ExpiresAt, + } + if err := result.Sign(key); err != nil { + t.Fatal(err) + } + sBytes, err := result.SigningBytes() + if err != nil { + t.Fatal(err) + } + aBytes, err := request.ConfigRef.SigningBytes() + if err != nil { + t.Fatal(err) + } + vectors := recoveryContractVectors{ + ValidationTime: recoveryNow, PublicKey: base64.StdEncoding.EncodeToString(key.Public().(ed25519.PublicKey)), + WrongPublicKey: base64.StdEncoding.EncodeToString(ed25519.NewKeyFromSeed(bytes.Repeat([]byte{8}, ed25519.SeedSize)).Public().(ed25519.PublicKey)), + Manifest: makeRecoveryVector(m, mBytes), ManifestSignature: mSignature, + Receipt: makeRecoveryVector(receipt, rBytes), ReceiptBinding: receipt.Binding(), + Artifact: makeRecoveryVector(request.ConfigRef, aBytes), Request: makeRecoveryVector(request, qBytes), + Result: makeRecoveryVector(result, sBytes), Preflight: preflightFixture(), + } + for _, mutation := range []struct { + name string + apply func(*protocol.AgentSetupInstallReceipt) + }{ + {"substituted-manifest", func(r *protocol.AgentSetupInstallReceipt) { r.ManifestDigest = "sha256:" + strings.Repeat("b", 64) }}, + {"replayed-session", func(r *protocol.AgentSetupInstallReceipt) { r.InstallSessionID = "other-session" }}, + {"cross-worker", func(r *protocol.AgentSetupInstallReceipt) { r.WorkerID = "other-worker" }}, + {"changed-challenge", func(r *protocol.AgentSetupInstallReceipt) { + r.Challenge = base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{10}, 32)) + }}, + {"expired", func(r *protocol.AgentSetupInstallReceipt) { + r.ExpiresAt = recoveryNow + r.CompletedAt = recoveryNow.Add(-time.Second) + }}, + } { + changed := receipt + mutation.apply(&changed) + if err := changed.Sign(key); err != nil { + t.Fatal(err) + } + vectors.ReceiptRejections = append(vectors.ReceiptRejections, recoveryRejection[protocol.AgentSetupInstallReceipt]{Name: mutation.name, Message: changed}) + } + for _, mutation := range []struct { + name string + apply func(*protocol.ManagedProviderLifecycleRequest) + }{ + {"prior-state", func(r *protocol.ManagedProviderLifecycleRequest) { + r.PriorStateHash = "sha256:" + strings.Repeat("b", 64) + }}, + {"config-content", func(r *protocol.ManagedProviderLifecycleRequest) { + r.ConfigRef.Digest = "sha256:" + strings.Repeat("b", 64) + r.ConfigHash = r.ConfigRef.Digest + }}, + {"artifact-location", func(r *protocol.ManagedProviderLifecycleRequest) { r.ConfigRef.Ref = "/api/artifacts/config-2" }}, + } { + changed := request + mutation.apply(&changed) + if err := changed.ConfigRef.Sign(key); err != nil { + t.Fatal(err) + } + if err := changed.Sign(key); err != nil { + t.Fatal(err) + } + vectors.ChangedRequests = append(vectors.ChangedRequests, recoveryRejection[protocol.ManagedProviderLifecycleRequest]{Name: mutation.name, Message: changed}) + } + return vectors +} + +func TestRecoveryContractVectors_SharedConsumerBytes(t *testing.T) { + const path = "testdata/recovery_contract_vectors.json" + want := exportedRecoveryVectors(t) + wantJSON, err := json.MarshalIndent(want, "", " ") + if err != nil { + t.Fatal(err) + } + wantJSON = append(wantJSON, '\n') + if *updateRecoveryVectors { + if err := os.MkdirAll("testdata", 0755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(path, wantJSON, 0644); err != nil { + t.Fatal(err) + } + } + data, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(data, wantJSON) { + t.Fatal("shared contract vector bytes/digests changed") + } + var consumer recoveryContractVectors + if err := protocol.DecodeStrict(bytes.NewReader(data), &consumer); err != nil { + t.Fatal(err) + } + public, err := base64.StdEncoding.DecodeString(consumer.PublicKey) + if err != nil { + t.Fatal(err) + } + for _, err := range []error{ + consumer.Manifest.Message.Verify(consumer.ManifestSignature, public, consumer.ValidationTime), + consumer.Receipt.Message.Verify(public, consumer.ReceiptBinding, consumer.ValidationTime), + consumer.Artifact.Message.Verify(public, consumer.ValidationTime), + consumer.Request.Message.Verify(public, consumer.ValidationTime), + consumer.Result.Message.Verify(public, consumer.Request.Message, consumer.ValidationTime), + consumer.Preflight.Validate(), + } { + if err != nil { + t.Fatal(err) + } + } + protoBytes, err := base64.StdEncoding.DecodeString(consumer.Receipt.SigningBase64) + if err != nil { + t.Fatal(err) + } + if _, err := protocol.UnmarshalAgentSetupInstallReceiptPayloadStrict(protoBytes); err != nil { + t.Fatal(err) + } + wrongPublic, err := base64.StdEncoding.DecodeString(consumer.WrongPublicKey) + if err != nil { + t.Fatal(err) + } + if err := consumer.Receipt.Message.Verify(wrongPublic, consumer.ReceiptBinding, consumer.ValidationTime); err == nil { + t.Fatal("shared wrong-key vector accepted") + } + for _, vector := range consumer.ReceiptRejections { + if err := vector.Message.Verify(public, consumer.ReceiptBinding, consumer.ValidationTime); err == nil { + t.Fatalf("shared receipt rejection accepted: %s", vector.Name) + } + } + for _, vector := range consumer.ChangedRequests { + if err := vector.Message.Verify(public, consumer.ValidationTime); err != nil { + t.Fatalf("changed request should have a valid signature: %s: %v", vector.Name, err) + } + if err := consumer.Result.Message.Verify(public, vector.Message, consumer.ValidationTime); err == nil { + t.Fatalf("shared changed-request result replay accepted: %s", vector.Name) + } + } +} diff --git a/protocol/testdata/recovery_contract_vectors.json b/protocol/testdata/recovery_contract_vectors.json new file mode 100644 index 0000000..d7cbd22 --- /dev/null +++ b/protocol/testdata/recovery_contract_vectors.json @@ -0,0 +1,533 @@ +{ + "validation_time": "2026-10-03T12:00:00Z", + "public_key": "6kpsY+KcUgq+9VB7Ey7F+ZVHdq6+vnuSQh7qaRRG0iw=", + "wrong_public_key": "E5j2LG0aRXxRumpLXz29L2n8qTIWIY3ImX5Ba9F9k8o=", + "manifest": { + "message": { + "protocol_version": "compute.v1alpha1", + "schema_version": "compute-bootstrap-manifest.v1", + "version": "v1.0.0", + "server_origin": "https://compute.example.test", + "wfctl_version": "v0.86.0", + "wfctl_assets": [ + { + "os": "linux", + "arch": "amd64", + "asset": { + "url": "https://releases.example.test/immutable/artifact", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + } + ], + "plugin_compute_version": "v1.0.0", + "plugin_compute_archive": { + "url": "https://releases.example.test/immutable/artifact", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "bootstrap": { + "url": "https://releases.example.test/immutable/artifact", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "package_id": "agent-core-linux-amd64", + "package_version": "v1.0.0", + "package_manifest_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "bootstrap_epoch": 1, + "package_epoch": 1, + "keyset_epoch": 1, + "keyset_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "package_signing_keys": [ + { + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "public_key": "6kpsY+KcUgq+9VB7Ey7F+ZVHdq6+vnuSQh7qaRRG0iw=" + } + ], + "expires_at": "2026-10-03T13:00:00Z" + }, + "signing_base64": "eyJwcm90b2NvbF92ZXJzaW9uIjoiY29tcHV0ZS52MWFscGhhMSIsInNjaGVtYV92ZXJzaW9uIjoiY29tcHV0ZS1ib290c3RyYXAtbWFuaWZlc3QudjEiLCJ2ZXJzaW9uIjoidjEuMC4wIiwic2VydmVyX29yaWdpbiI6Imh0dHBzOi8vY29tcHV0ZS5leGFtcGxlLnRlc3QiLCJ3ZmN0bF92ZXJzaW9uIjoidjAuODYuMCIsIndmY3RsX2Fzc2V0cyI6W3sib3MiOiJsaW51eCIsImFyY2giOiJhbWQ2NCIsImFzc2V0Ijp7InVybCI6Imh0dHBzOi8vcmVsZWFzZXMuZXhhbXBsZS50ZXN0L2ltbXV0YWJsZS9hcnRpZmFjdCIsImRpZ2VzdCI6InNoYTI1NjphYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhIn19XSwicGx1Z2luX2NvbXB1dGVfdmVyc2lvbiI6InYxLjAuMCIsInBsdWdpbl9jb21wdXRlX2FyY2hpdmUiOnsidXJsIjoiaHR0cHM6Ly9yZWxlYXNlcy5leGFtcGxlLnRlc3QvaW1tdXRhYmxlL2FydGlmYWN0IiwiZGlnZXN0Ijoic2hhMjU2OmFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEifSwiYm9vdHN0cmFwIjp7InVybCI6Imh0dHBzOi8vcmVsZWFzZXMuZXhhbXBsZS50ZXN0L2ltbXV0YWJsZS9hcnRpZmFjdCIsImRpZ2VzdCI6InNoYTI1NjphYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhIn0sInBhY2thZ2VfaWQiOiJhZ2VudC1jb3JlLWxpbnV4LWFtZDY0IiwicGFja2FnZV92ZXJzaW9uIjoidjEuMC4wIiwicGFja2FnZV9tYW5pZmVzdF9kaWdlc3QiOiJzaGEyNTY6YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYSIsImJvb3RzdHJhcF9lcG9jaCI6MSwicGFja2FnZV9lcG9jaCI6MSwia2V5c2V0X2Vwb2NoIjoxLCJrZXlzZXRfZGlnZXN0Ijoic2hhMjU2OmFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEiLCJwYWNrYWdlX3NpZ25pbmdfa2V5cyI6W3sia2V5X2lkIjoic2hhMjU2OmZlODEyYzEyZjNhYjRjZTZhYzVkYjY5YWMzNTJmOTA2Y2IxYjExZWY0M2ZiMzNlMjUyZWY3ZmY1NTIyNjM4ODkiLCJwdWJsaWNfa2V5IjoiNmtwc1krS2NVZ3ErOVZCN0V5N0YrWlZIZHE2K3ZudVNRaDdxYVJSRzBpdz0ifV0sImV4cGlyZXNfYXQiOiIyMDI2LTEwLTAzVDEzOjAwOjAwWiJ9", + "signing_digest": "sha256:ef14acb29a8414ed7dd581442a2082cf1b4ca46a933253f9efd277e8f63a7de8" + }, + "manifest_signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "P9krVAvk+PM1pKhem5xnEZO6dtDgK0cztp+XbLHUQvGMFZA8/jnbcd1HBzlGYnJrXqwBYwy3Ot6+8UYCjTuuAQ==" + }, + "receipt": { + "message": { + "protocol_version": "compute.v1alpha1", + "receipt_id": "receipt-1", + "challenge": "CQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQk=", + "invite_id": "invite-1", + "install_session_id": "session-1", + "worker_id": "worker-1", + "package_id": "agent-core-linux-amd64", + "package_version": "v1.0.0", + "package_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "manifest_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "bootstrap_epoch": 1, + "package_epoch": 1, + "keyset_epoch": 1, + "launcher_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "slot_id": "A", + "slot_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "service_unit_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "service_mode": "systemd-user", + "started_pid": 123, + "heartbeat_id": "heartbeat-1", + "heartbeat_at": "2026-10-03T11:59:00Z", + "cloud_final_unit": "cloud-final.service", + "scrub_unit": "compute-scrub.service", + "wrapper_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scan_hit_count": 0, + "code_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "completed_at": "2026-10-03T12:00:00Z", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "Y0PvaUyPwev2WtpFfxvdfQasXyyRzS/t9w09I9KwQX5Rgnba9rYJlOnnPsCeaZouhJB9Tb0ERGKovKu+aCjmCg==" + } + }, + "signing_base64": "Ch5hZ2VudC1zZXR1cC1pbnN0YWxsLXJlY2VpcHQudjESEGNvbXB1dGUudjFhbHBoYTEaCXJlY2VpcHQtMSIsQ1FrSkNRa0pDUWtKQ1FrSkNRa0pDUWtKQ1FrSkNRa0pDUWtKQ1FrSkNRaz0qCGludml0ZS0xMglzZXNzaW9uLTE6CHdvcmtlci0xQhZhZ2VudC1jb3JlLWxpbnV4LWFtZDY0SgZ2MS4wLjBSR3NoYTI1NjphYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhWkdzaGEyNTY6YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWABaAFwAXpHc2hhMjU2OmFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWGCAQFBigFHc2hhMjU2OmFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWGSAUdzaGEyNTY6YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYZoBDHN5c3RlbWQtdXNlcqABe6oBC2hlYXJ0YmVhdC0xsAGA0Nyx5eTA7Ri6ARNjbG91ZC1maW5hbC5zZXJ2aWNlwgEVY29tcHV0ZS1zY3J1Yi5zZXJ2aWNlygFHc2hhMjU2OmFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWHaAUdzaGEyNTY6YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYeABgID688TmwO0Y6AGAwNz5p8/B7Rg=", + "signing_digest": "sha256:58576c7e80ba3f0eb88de2d01cabbc3a835e6bc0145221d20c1d412049e0f283" + }, + "receipt_binding": { + "challenge": "CQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQk=", + "invite_id": "invite-1", + "install_session_id": "session-1", + "worker_id": "worker-1", + "package_id": "agent-core-linux-amd64", + "package_version": "v1.0.0", + "package_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "manifest_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "bootstrap_epoch": 1, + "package_epoch": 1, + "keyset_epoch": 1, + "expires_at": "2026-10-03T13:00:00Z" + }, + "artifact": { + "message": { + "protocol_version": "compute.v1alpha1", + "transaction_id": "transaction-1", + "worker_id": "worker-1", + "plugin_id": "workflow-plugin-example", + "component": "provider-binary", + "component_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref": "/api/artifacts/config-1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "max_bytes": 4096, + "envelope_key_id": "lifecycle-key-1", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "r0Fy+6bZXIfLUTSwgQk15GXvkFc4cf+SFadkFBxFnuZTjOO2aVoB3fJj2MJffMhYSEYuYK4YAPdtt0jzxbTFCg==" + } + }, + "signing_base64": "eyJkb21haW4iOiJtYW5hZ2VkLWxpZmVjeWNsZS1hcnRpZmFjdC52MSIsInBheWxvYWQiOnsicHJvdG9jb2xfdmVyc2lvbiI6ImNvbXB1dGUudjFhbHBoYTEiLCJ0cmFuc2FjdGlvbl9pZCI6InRyYW5zYWN0aW9uLTEiLCJ3b3JrZXJfaWQiOiJ3b3JrZXItMSIsInBsdWdpbl9pZCI6IndvcmtmbG93LXBsdWdpbi1leGFtcGxlIiwiY29tcG9uZW50IjoicHJvdmlkZXItYmluYXJ5IiwiY29tcG9uZW50X2RpZ2VzdCI6InNoYTI1NjphYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhIiwicmVmIjoiL2FwaS9hcnRpZmFjdHMvY29uZmlnLTEiLCJkaWdlc3QiOiJzaGEyNTY6YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYSIsIm1heF9ieXRlcyI6NDA5NiwiZW52ZWxvcGVfa2V5X2lkIjoibGlmZWN5Y2xlLWtleS0xIiwiZXhwaXJlc19hdCI6IjIwMjYtMTAtMDNUMTM6MDA6MDBaIn19", + "signing_digest": "sha256:a7543d845979c21c93ebe6fa5ba5ec79563c23346097911f756d890f1be94328" + }, + "request": { + "message": { + "protocol_version": "compute.v1alpha1", + "action": "prepare", + "required_capability": "managed-provider-lifecycle-v1", + "transaction_id": "transaction-1", + "worker_id": "worker-1", + "plugin_id": "workflow-plugin-example", + "component": "provider-binary", + "version": "v1.0.0", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "config_ref": { + "protocol_version": "compute.v1alpha1", + "transaction_id": "transaction-1", + "worker_id": "worker-1", + "plugin_id": "workflow-plugin-example", + "component": "provider-binary", + "component_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref": "/api/artifacts/config-1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "max_bytes": 4096, + "envelope_key_id": "lifecycle-key-1", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "r0Fy+6bZXIfLUTSwgQk15GXvkFc4cf+SFadkFBxFnuZTjOO2aVoB3fJj2MJffMhYSEYuYK4YAPdtt0jzxbTFCg==" + } + }, + "config_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "secret_refs": [], + "prior_state_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "zmguKdF5u+mzwan3Cd8ZFBDRURmP1YvrOyIhWi2Qi8D9QsoyBj3G8Nk0WPKG3L/smjbvPva4GoIFJ/hc3h9jDw==" + } + }, + "signing_base64": "eyJkb21haW4iOiJtYW5hZ2VkLXByb3ZpZGVyLWxpZmVjeWNsZS1yZXF1ZXN0LnYxIiwicGF5bG9hZCI6eyJwcm90b2NvbF92ZXJzaW9uIjoiY29tcHV0ZS52MWFscGhhMSIsImFjdGlvbiI6InByZXBhcmUiLCJyZXF1aXJlZF9jYXBhYmlsaXR5IjoibWFuYWdlZC1wcm92aWRlci1saWZlY3ljbGUtdjEiLCJ0cmFuc2FjdGlvbl9pZCI6InRyYW5zYWN0aW9uLTEiLCJ3b3JrZXJfaWQiOiJ3b3JrZXItMSIsInBsdWdpbl9pZCI6IndvcmtmbG93LXBsdWdpbi1leGFtcGxlIiwiY29tcG9uZW50IjoicHJvdmlkZXItYmluYXJ5IiwidmVyc2lvbiI6InYxLjAuMCIsImRpZ2VzdCI6InNoYTI1NjphYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhIiwiY29uZmlnX3JlZiI6eyJwcm90b2NvbF92ZXJzaW9uIjoiY29tcHV0ZS52MWFscGhhMSIsInRyYW5zYWN0aW9uX2lkIjoidHJhbnNhY3Rpb24tMSIsIndvcmtlcl9pZCI6Indvcmtlci0xIiwicGx1Z2luX2lkIjoid29ya2Zsb3ctcGx1Z2luLWV4YW1wbGUiLCJjb21wb25lbnQiOiJwcm92aWRlci1iaW5hcnkiLCJjb21wb25lbnRfZGlnZXN0Ijoic2hhMjU2OmFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEiLCJyZWYiOiIvYXBpL2FydGlmYWN0cy9jb25maWctMSIsImRpZ2VzdCI6InNoYTI1NjphYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhIiwibWF4X2J5dGVzIjo0MDk2LCJlbnZlbG9wZV9rZXlfaWQiOiJsaWZlY3ljbGUta2V5LTEiLCJleHBpcmVzX2F0IjoiMjAyNi0xMC0wM1QxMzowMDowMFoiLCJzaWduYXR1cmUiOnsiYWxnb3JpdGhtIjoiZWQyNTUxOSIsImtleV9pZCI6InNoYTI1NjpmZTgxMmMxMmYzYWI0Y2U2YWM1ZGI2OWFjMzUyZjkwNmNiMWIxMWVmNDNmYjMzZTI1MmVmN2ZmNTUyMjYzODg5IiwidmFsdWUiOiJyMEZ5KzZiWlhJZkxVVFN3Z1FrMTVHWHZrRmM0Y2YrU0ZhZGtGQnhGbnVaVGpPTzJhVm9CM2ZKajJNSmZmTWhZU0VZdVlLNFlBUGR0dDBqenhiVEZDZz09In19LCJjb25maWdfaGFzaCI6InNoYTI1NjphYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhIiwic2VjcmV0X3JlZnMiOltdLCJwcmlvcl9zdGF0ZV9oYXNoIjoic2hhMjU2OmFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEiLCJleHBpcmVzX2F0IjoiMjAyNi0xMC0wM1QxMzowMDowMFoifX0=", + "signing_digest": "sha256:877186e85c723da465724fa14112d268cbbed7b772dfd30cb750b1058356e3f0" + }, + "result": { + "message": { + "protocol_version": "compute.v1alpha1", + "transaction_id": "transaction-1", + "worker_id": "worker-1", + "plugin_id": "workflow-plugin-example", + "component": "provider-binary", + "version": "v1.0.0", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "action": "prepare", + "request_hash": "sha256:877186e85c723da465724fa14112d268cbbed7b772dfd30cb750b1058356e3f0", + "idempotency_state": "prepared", + "service_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "state_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "status": "succeeded", + "rollback_result": "not-required", + "completed_at": "2026-10-03T12:00:00Z", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "S2nktCGjgTK1wNuDcqQJNTPqSNkC13E3u2UKgQAfsbBmzPSbSiqgNp0VqlR29qoh7Xj6oeJu5B7xkY4JY5qvDg==" + } + }, + "signing_base64": "eyJkb21haW4iOiJtYW5hZ2VkLXByb3ZpZGVyLWxpZmVjeWNsZS1yZXN1bHQudjEiLCJwYXlsb2FkIjp7InByb3RvY29sX3ZlcnNpb24iOiJjb21wdXRlLnYxYWxwaGExIiwidHJhbnNhY3Rpb25faWQiOiJ0cmFuc2FjdGlvbi0xIiwid29ya2VyX2lkIjoid29ya2VyLTEiLCJwbHVnaW5faWQiOiJ3b3JrZmxvdy1wbHVnaW4tZXhhbXBsZSIsImNvbXBvbmVudCI6InByb3ZpZGVyLWJpbmFyeSIsInZlcnNpb24iOiJ2MS4wLjAiLCJkaWdlc3QiOiJzaGEyNTY6YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYSIsImFjdGlvbiI6InByZXBhcmUiLCJyZXF1ZXN0X2hhc2giOiJzaGEyNTY6ODc3MTg2ZTg1YzcyM2RhNDY1NzI0ZmExNDExMmQyNjhjYmJlZDdiNzcyZGZkMzBjYjc1MGIxMDU4MzU2ZTNmMCIsImlkZW1wb3RlbmN5X3N0YXRlIjoicHJlcGFyZWQiLCJzZXJ2aWNlX2hhc2giOiJzaGEyNTY6YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYSIsInN0YXRlX2hhc2giOiJzaGEyNTY6YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYSIsInN0YXR1cyI6InN1Y2NlZWRlZCIsInJvbGxiYWNrX3Jlc3VsdCI6Im5vdC1yZXF1aXJlZCIsImNvbXBsZXRlZF9hdCI6IjIwMjYtMTAtMDNUMTI6MDA6MDBaIiwiZXhwaXJlc19hdCI6IjIwMjYtMTAtMDNUMTM6MDA6MDBaIn19", + "signing_digest": "sha256:196b9332914ea78670f2af054b4c3ca9abade072894f5cbf887eb41718cce8ec" + }, + "preflight": { + "protocol_version": "dedicated-provider-host-preflight.v1", + "worker_id": "worker-1", + "os": "linux", + "uid": 1000, + "home_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "machine_id_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "agent_unit_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "launcher_unit_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "firewall_policy_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "agent_service_state": "active", + "launcher_service_state": "active", + "user_bus_available": true, + "lingering_enabled": true, + "rootless_podman": true, + "actions_runner_process_present": false, + "actions_runner_service_present": false + }, + "receipt_rejections": [ + { + "name": "substituted-manifest", + "message": { + "protocol_version": "compute.v1alpha1", + "receipt_id": "receipt-1", + "challenge": "CQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQk=", + "invite_id": "invite-1", + "install_session_id": "session-1", + "worker_id": "worker-1", + "package_id": "agent-core-linux-amd64", + "package_version": "v1.0.0", + "package_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "manifest_digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "bootstrap_epoch": 1, + "package_epoch": 1, + "keyset_epoch": 1, + "launcher_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "slot_id": "A", + "slot_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "service_unit_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "service_mode": "systemd-user", + "started_pid": 123, + "heartbeat_id": "heartbeat-1", + "heartbeat_at": "2026-10-03T11:59:00Z", + "cloud_final_unit": "cloud-final.service", + "scrub_unit": "compute-scrub.service", + "wrapper_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scan_hit_count": 0, + "code_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "completed_at": "2026-10-03T12:00:00Z", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "XX1mdxdgpDVeIJ8nTFRSCMTmkw9XS6OsW5qoUYYB7Kefe4hi424WpRmSu98hN1V6ImdnXpc32r+aXO1WrJVnAg==" + } + } + }, + { + "name": "replayed-session", + "message": { + "protocol_version": "compute.v1alpha1", + "receipt_id": "receipt-1", + "challenge": "CQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQk=", + "invite_id": "invite-1", + "install_session_id": "other-session", + "worker_id": "worker-1", + "package_id": "agent-core-linux-amd64", + "package_version": "v1.0.0", + "package_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "manifest_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "bootstrap_epoch": 1, + "package_epoch": 1, + "keyset_epoch": 1, + "launcher_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "slot_id": "A", + "slot_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "service_unit_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "service_mode": "systemd-user", + "started_pid": 123, + "heartbeat_id": "heartbeat-1", + "heartbeat_at": "2026-10-03T11:59:00Z", + "cloud_final_unit": "cloud-final.service", + "scrub_unit": "compute-scrub.service", + "wrapper_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scan_hit_count": 0, + "code_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "completed_at": "2026-10-03T12:00:00Z", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "1VfvHGHJ9zFL5Ae36iJCc4nrV5B6RDP6FKyfqNSQ0WSBQpjznINRW6xfzH9Kfd9sp5+0TjAg+1eWqUgm0UQuBA==" + } + } + }, + { + "name": "cross-worker", + "message": { + "protocol_version": "compute.v1alpha1", + "receipt_id": "receipt-1", + "challenge": "CQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQk=", + "invite_id": "invite-1", + "install_session_id": "session-1", + "worker_id": "other-worker", + "package_id": "agent-core-linux-amd64", + "package_version": "v1.0.0", + "package_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "manifest_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "bootstrap_epoch": 1, + "package_epoch": 1, + "keyset_epoch": 1, + "launcher_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "slot_id": "A", + "slot_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "service_unit_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "service_mode": "systemd-user", + "started_pid": 123, + "heartbeat_id": "heartbeat-1", + "heartbeat_at": "2026-10-03T11:59:00Z", + "cloud_final_unit": "cloud-final.service", + "scrub_unit": "compute-scrub.service", + "wrapper_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scan_hit_count": 0, + "code_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "completed_at": "2026-10-03T12:00:00Z", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "S5vUAsDNOVQXmlCQT0CEoFeGayLE8GIabmb7XgIRxwVEoTPQGTyrxfMk5LnIBqoloL1aWTb3fZaUpDa5op0oCw==" + } + } + }, + { + "name": "changed-challenge", + "message": { + "protocol_version": "compute.v1alpha1", + "receipt_id": "receipt-1", + "challenge": "CgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgo=", + "invite_id": "invite-1", + "install_session_id": "session-1", + "worker_id": "worker-1", + "package_id": "agent-core-linux-amd64", + "package_version": "v1.0.0", + "package_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "manifest_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "bootstrap_epoch": 1, + "package_epoch": 1, + "keyset_epoch": 1, + "launcher_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "slot_id": "A", + "slot_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "service_unit_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "service_mode": "systemd-user", + "started_pid": 123, + "heartbeat_id": "heartbeat-1", + "heartbeat_at": "2026-10-03T11:59:00Z", + "cloud_final_unit": "cloud-final.service", + "scrub_unit": "compute-scrub.service", + "wrapper_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scan_hit_count": 0, + "code_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "completed_at": "2026-10-03T12:00:00Z", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "7Nmvl/S+CFXA6aZ+twJYN/mJXYLVfm6dD5hnbEkHgGAUUvmI9gwsaCJ3NPOuY8ylHNrYoXUQ7N/lKCDiS4qNBQ==" + } + } + }, + { + "name": "expired", + "message": { + "protocol_version": "compute.v1alpha1", + "receipt_id": "receipt-1", + "challenge": "CQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQk=", + "invite_id": "invite-1", + "install_session_id": "session-1", + "worker_id": "worker-1", + "package_id": "agent-core-linux-amd64", + "package_version": "v1.0.0", + "package_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "manifest_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "bootstrap_epoch": 1, + "package_epoch": 1, + "keyset_epoch": 1, + "launcher_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "slot_id": "A", + "slot_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "service_unit_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "service_mode": "systemd-user", + "started_pid": 123, + "heartbeat_id": "heartbeat-1", + "heartbeat_at": "2026-10-03T11:59:00Z", + "cloud_final_unit": "cloud-final.service", + "scrub_unit": "compute-scrub.service", + "wrapper_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scan_hit_count": 0, + "code_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "completed_at": "2026-10-03T11:59:59Z", + "expires_at": "2026-10-03T12:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "06PkaiOhDEV/2W6Pie3+QJhpYtfjT7RNhI0wpuVToyK/VcmgjM0qWHiNTAg5c6NDLmdwxq88rOExRyTJSGs6DA==" + } + } + } + ], + "changed_requests_for_result": [ + { + "name": "prior-state", + "message": { + "protocol_version": "compute.v1alpha1", + "action": "prepare", + "required_capability": "managed-provider-lifecycle-v1", + "transaction_id": "transaction-1", + "worker_id": "worker-1", + "plugin_id": "workflow-plugin-example", + "component": "provider-binary", + "version": "v1.0.0", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "config_ref": { + "protocol_version": "compute.v1alpha1", + "transaction_id": "transaction-1", + "worker_id": "worker-1", + "plugin_id": "workflow-plugin-example", + "component": "provider-binary", + "component_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref": "/api/artifacts/config-1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "max_bytes": 4096, + "envelope_key_id": "lifecycle-key-1", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "r0Fy+6bZXIfLUTSwgQk15GXvkFc4cf+SFadkFBxFnuZTjOO2aVoB3fJj2MJffMhYSEYuYK4YAPdtt0jzxbTFCg==" + } + }, + "config_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "secret_refs": [], + "prior_state_hash": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "i5qtQfB4VOxnbDdGtMaQwLS3DPMhLBaQ7OuJNcoU8TTatgadCpW+eFBRnB9ogbu/nC1Mq2nCKxi/SA1O5YN2AA==" + } + } + }, + { + "name": "config-content", + "message": { + "protocol_version": "compute.v1alpha1", + "action": "prepare", + "required_capability": "managed-provider-lifecycle-v1", + "transaction_id": "transaction-1", + "worker_id": "worker-1", + "plugin_id": "workflow-plugin-example", + "component": "provider-binary", + "version": "v1.0.0", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "config_ref": { + "protocol_version": "compute.v1alpha1", + "transaction_id": "transaction-1", + "worker_id": "worker-1", + "plugin_id": "workflow-plugin-example", + "component": "provider-binary", + "component_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref": "/api/artifacts/config-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "max_bytes": 4096, + "envelope_key_id": "lifecycle-key-1", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "Nz2rCpGgop8eUszP87H3vhx7R6qshZRHCVqu+X2HIa6RHnAivBDEzTgwvoDCHtWQ1zps7Q/7VTKyc3ltjI5DAQ==" + } + }, + "config_hash": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "secret_refs": [], + "prior_state_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "km2YrhE7xiKlbSD5MSvSH1BikikFcCM3rd2pPdhPyScUbBMKhylsXbJSf6X2EyiHS+GfVjz+vGTQ79l2y1ndAg==" + } + } + }, + { + "name": "artifact-location", + "message": { + "protocol_version": "compute.v1alpha1", + "action": "prepare", + "required_capability": "managed-provider-lifecycle-v1", + "transaction_id": "transaction-1", + "worker_id": "worker-1", + "plugin_id": "workflow-plugin-example", + "component": "provider-binary", + "version": "v1.0.0", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "config_ref": { + "protocol_version": "compute.v1alpha1", + "transaction_id": "transaction-1", + "worker_id": "worker-1", + "plugin_id": "workflow-plugin-example", + "component": "provider-binary", + "component_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref": "/api/artifacts/config-2", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "max_bytes": 4096, + "envelope_key_id": "lifecycle-key-1", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "LmCHcWJi7Fw5q7rgwuHGL3vKk8X+F9HD/c1otcertGICVSxvlcCt4RK09dsMj0YcivtyAC4c26Pd1bmQMug9Bg==" + } + }, + "config_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "secret_refs": [], + "prior_state_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "expires_at": "2026-10-03T13:00:00Z", + "signature": { + "algorithm": "ed25519", + "key_id": "sha256:fe812c12f3ab4ce6ac5db69ac352f906cb1b11ef43fb33e252ef7ff552263889", + "value": "RQFz5nkxZvEiSeZ/UEDuG+OH9P+UwUsQLsfLwG0xL0szriN9V+g+CdO9WrXM3Omc8Xry+oFcCEgiWx1bt7p9DA==" + } + } + } + ] +}