diff --git a/Cargo.lock b/Cargo.lock index 012faedf8f3..b76e415971e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9400,8 +9400,11 @@ dependencies = [ "anyhow", "async-process", "bytes", + "doc", "futures", "insta", + "json", + "json-patch", "locate-bin", "models", "serde", diff --git a/crates/doc/src/annotation.rs b/crates/doc/src/annotation.rs index 9902e22a27f..1cb185ac81e 100644 --- a/crates/doc/src/annotation.rs +++ b/crates/doc/src/annotation.rs @@ -14,6 +14,11 @@ pub enum Annotation { Redact(redact::Strategy), /// "secret" or "airbyte_secret" annotation keyword. Secret(bool), + /// "nonsensitive" annotation keyword. Marks a location (or subtree) as not + /// security-relevant, meaning it may be carried in a plaintext `sops.overlay` + /// and modified without re-encrypting the configuration. This annotation must + /// only be added after human review, un-annotated locations are treated as sensitive. + Nonsensitive(bool), /// "multiline" annotation keyword marks fields that should have a multiline text input in the /// UI. This is from the airbyte spec. Multiline(bool), @@ -41,6 +46,7 @@ impl schema::Annotation for Annotation { Annotation::Reduce(_) => "reduce", Annotation::Redact(_) => "redact", Annotation::Secret(_) => "secret", + Annotation::Nonsensitive(_) => "nonsensitive", Annotation::Multiline(_) => "multiline", Annotation::Advanced(_) => "advanced", Annotation::Order(_) => "order", @@ -54,8 +60,8 @@ impl schema::Annotation for Annotation { // `x-str-minimum` / `x-str-maximum` are enforced validation keywords // not annotations so we need to preempt the starts_with("x-") check. schema::keywords::X_STR_MINIMUM | schema::keywords::X_STR_MAXIMUM => false, - "reduce" | "redact" | "secret" | "airbyte_secret" | "multiline" | "advanced" - | "order" | "discriminator" => true, + "reduce" | "redact" | "secret" | "airbyte_secret" | "nonsensitive" | "multiline" + | "advanced" | "order" | "discriminator" => true, key if key.starts_with("x-") || key.starts_with("X-") => true, _ => schema::CoreAnnotation::uses_keyword(keyword), } @@ -69,6 +75,7 @@ impl schema::Annotation for Annotation { "redact" => Ok(Annotation::Redact(redact::Strategy::try_from(value)?)), "order" => Ok(Annotation::Order(i32::deserialize(value)?)), "secret" | "airbyte_secret" => Ok(Annotation::Secret(bool::deserialize(value)?)), + "nonsensitive" => Ok(Annotation::Nonsensitive(bool::deserialize(value)?)), "multiline" => Ok(Annotation::Multiline(bool::deserialize(value)?)), "advanced" => Ok(Annotation::Advanced(bool::deserialize(value)?)), "discriminator" => Ok(Annotation::Discriminator(value.clone())), diff --git a/crates/doc/src/shape/inference.rs b/crates/doc/src/shape/inference.rs index 3a3265a7bc7..3ebbd9409fa 100644 --- a/crates/doc/src/shape/inference.rs +++ b/crates/doc/src/shape/inference.rs @@ -241,6 +241,7 @@ impl Shape { // but explicitly mentioned so that a compiler error will force us to check // here as new annotations are added. Annotation::Secret(b) => shape.secret = Some(*b), + Annotation::Nonsensitive(b) => shape.nonsensitive = Some(*b), Annotation::Multiline(_) => {} Annotation::Advanced(_) => {} Annotation::Order(_) => {} @@ -556,6 +557,7 @@ mod test { None, ))), secret: Some(true), + nonsensitive: None, content_media_type: Some("some/thing".into()), string: StringShape { content_encoding: Some("base64".into()), @@ -771,6 +773,7 @@ mod test { provenance: Inline, default: None, secret: None, + nonsensitive: None, content_media_type: None, annotations: {}, array: ArrayShape { diff --git a/crates/doc/src/shape/intersect.rs b/crates/doc/src/shape/intersect.rs index 55d3e135799..ceec98307da 100644 --- a/crates/doc/src/shape/intersect.rs +++ b/crates/doc/src/shape/intersect.rs @@ -268,6 +268,7 @@ impl Shape { let provenance = lhs.provenance.intersect(rhs.provenance); let default = lhs.default.or(rhs.default); let secret = lhs.secret.or(rhs.secret); + let nonsensitive = lhs.nonsensitive.or(rhs.nonsensitive); let content_media_type = lhs.content_media_type.or(rhs.content_media_type); @@ -313,6 +314,7 @@ impl Shape { provenance, default, secret, + nonsensitive, content_media_type, annotations, string, diff --git a/crates/doc/src/shape/mod.rs b/crates/doc/src/shape/mod.rs index c50cb24840f..9e73d6dd0e2 100644 --- a/crates/doc/src/shape/mod.rs +++ b/crates/doc/src/shape/mod.rs @@ -36,6 +36,10 @@ pub struct Shape { pub default: Option)>>, /// Is this location sensitive? For example, a password or credential. pub secret: Option, + /// Is this location explicitly marked as not security-relevant? Such locations + /// (and their subtrees) may be carried in a plaintext `sops.overlay` and modified + /// without re-encrypting the configuration. `None` is treated as sensitive. + pub nonsensitive: Option, /// Annotated `contentMediaType`. The JSON-Schema specification defines /// this annotation only for strings; Flow extends it to apply to any /// type, so it lives at the top level rather than nested in a typed @@ -196,6 +200,7 @@ impl Shape { provenance: Provenance::Unset, default: None, secret: None, + nonsensitive: None, content_media_type: None, annotations: BTreeMap::new(), array: ArrayShape::new(), @@ -218,6 +223,7 @@ impl Shape { provenance: Provenance::Inline, default: None, secret: None, + nonsensitive: None, content_media_type: None, annotations: BTreeMap::new(), array: ArrayShape::new(), diff --git a/crates/doc/src/shape/schema.rs b/crates/doc/src/shape/schema.rs index 1933d55120e..4e74ca5f5f0 100644 --- a/crates/doc/src/shape/schema.rs +++ b/crates/doc/src/shape/schema.rs @@ -18,6 +18,7 @@ fn to_sub_schema(shape: Shape) -> Schema { provenance: _, // Not mapped to a schema. default, secret, + nonsensitive, content_media_type, annotations, array, @@ -202,6 +203,9 @@ fn to_sub_schema(shape: Shape) -> Schema { if let Some(true) = secret { out.insert("secret".to_string(), serde_json::json!(true)); } + if let Some(true) = nonsensitive { + out.insert("nonsensitive".to_string(), serde_json::json!(true)); + } match reduce { Reduce::Unset | Reduce::Multiple => {} diff --git a/crates/doc/src/shape/snapshots/doc__shape__inference__test__annotation_collection.snap b/crates/doc/src/shape/snapshots/doc__shape__inference__test__annotation_collection.snap index 53cff8bf8a9..aeac9db2fc0 100644 --- a/crates/doc/src/shape/snapshots/doc__shape__inference__test__annotation_collection.snap +++ b/crates/doc/src/shape/snapshots/doc__shape__inference__test__annotation_collection.snap @@ -12,6 +12,7 @@ Shape { provenance: Inline, default: None, secret: None, + nonsensitive: None, content_media_type: None, annotations: { "x-test-top-level": Bool(true), @@ -44,6 +45,7 @@ Shape { provenance: Inline, default: None, secret: None, + nonsensitive: None, content_media_type: None, annotations: { "X-bar-top-level": Bool(true), @@ -90,6 +92,7 @@ Shape { provenance: Inline, default: None, secret: None, + nonsensitive: None, content_media_type: None, annotations: { "x-conflicting-ann": String("yes please"), @@ -133,6 +136,7 @@ Shape { provenance: Inline, default: None, secret: None, + nonsensitive: None, content_media_type: None, annotations: { "X-foo-top-level": Bool(false), diff --git a/crates/doc/src/shape/union.rs b/crates/doc/src/shape/union.rs index 65ab52f5bf8..5e64156f6e3 100644 --- a/crates/doc/src/shape/union.rs +++ b/crates/doc/src/shape/union.rs @@ -283,6 +283,7 @@ impl Shape { let provenance = lhs.provenance.union(rhs.provenance); let default = union_option(lhs.default, rhs.default); let secret = union_option(lhs.secret, rhs.secret); + let nonsensitive = union_option(lhs.nonsensitive, rhs.nonsensitive); let content_media_type = union_option(lhs.content_media_type, rhs.content_media_type); // Union of annotations is actually an _intersection_, which yields only @@ -333,6 +334,7 @@ impl Shape { provenance, default, secret, + nonsensitive, content_media_type, annotations, string, diff --git a/crates/flowctl/src/raw/materialize_fixture.rs b/crates/flowctl/src/raw/materialize_fixture.rs index 0a3594f4ef7..48ca6a682c2 100644 --- a/crates/flowctl/src/raw/materialize_fixture.rs +++ b/crates/flowctl/src/raw/materialize_fixture.rs @@ -72,6 +72,7 @@ pub async fn do_materialize_fixture( }), state_json: checkpoint.to_string().into(), version: "test".to_string(), + sealed_config_json: Default::default(), }), ..Default::default() }); diff --git a/crates/proto-flow/src/capture.rs b/crates/proto-flow/src/capture.rs index cfc24c54add..72c7c9999cb 100644 --- a/crates/proto-flow/src/capture.rs +++ b/crates/proto-flow/src/capture.rs @@ -147,6 +147,13 @@ pub mod request { /// Last-persisted connector checkpoint state from a previous invocation. #[prost(bytes = "bytes", tag = "4")] pub state_json: ::prost::bytes::Bytes, + /// Sealed (encrypted) endpoint configuration of this capture. + /// This is the SOPS-encrypted document from which the runtime derived the + /// decrypted `capture.config_json`, including any `sops` metadata stanza. + /// Connectors may reuse it to emit `configUpdate`s that modify nonsensitive + /// overlay fields without re-encrypting the configuration. + #[prost(bytes = "bytes", tag = "5")] + pub sealed_config_json: ::prost::bytes::Bytes, } /// Tell the connector that some number of its preceding Checkpoints have /// committed to the Flow recovery log. diff --git a/crates/proto-flow/src/capture.serde.rs b/crates/proto-flow/src/capture.serde.rs index 4b5d3f18a6e..f9d25f0a408 100644 --- a/crates/proto-flow/src/capture.serde.rs +++ b/crates/proto-flow/src/capture.serde.rs @@ -624,6 +624,9 @@ impl serde::Serialize for request::Open { if !self.state_json.is_empty() { len += 1; } + if !self.sealed_config_json.is_empty() { + len += 1; + } let mut struct_ser = serializer.serialize_struct("capture.Request.Open", len)?; if let Some(v) = self.capture.as_ref() { struct_ser.serialize_field("capture", v)?; @@ -639,6 +642,11 @@ impl serde::Serialize for request::Open { #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("state", &crate::as_raw_json(&self.state_json)?)?; } + if !self.sealed_config_json.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("sealedConfig", &crate::as_raw_json(&self.sealed_config_json)?)?; + } struct_ser.end() } } @@ -654,6 +662,8 @@ impl<'de> serde::Deserialize<'de> for request::Open { "range", "state_json", "state", + "sealed_config_json", + "sealedConfig", ]; #[allow(clippy::enum_variant_names)] @@ -662,6 +672,7 @@ impl<'de> serde::Deserialize<'de> for request::Open { Version, Range, StateJson, + SealedConfigJson, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -688,6 +699,7 @@ impl<'de> serde::Deserialize<'de> for request::Open { "version" => Ok(GeneratedField::Version), "range" => Ok(GeneratedField::Range), "state" | "state_json" => Ok(GeneratedField::StateJson), + "sealedConfig" | "sealed_config_json" => Ok(GeneratedField::SealedConfigJson), _ => Ok(GeneratedField::__SkipField__), } } @@ -711,6 +723,7 @@ impl<'de> serde::Deserialize<'de> for request::Open { let mut version__ = None; let mut range__ = None; let mut state_json__ = None; + let mut sealed_config_json__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Capture => { @@ -739,6 +752,14 @@ impl<'de> serde::Deserialize<'de> for request::Open { Some(map_.next_value::()?.0) ; } + GeneratedField::SealedConfigJson => { + if sealed_config_json__.is_some() { + return Err(serde::de::Error::duplicate_field("sealedConfig")); + } + sealed_config_json__ = + Some(map_.next_value::()?.0) + ; + } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } @@ -749,6 +770,7 @@ impl<'de> serde::Deserialize<'de> for request::Open { version: version__.unwrap_or_default(), range: range__, state_json: state_json__.unwrap_or_default(), + sealed_config_json: sealed_config_json__.unwrap_or_default(), }) } } diff --git a/crates/proto-flow/src/materialize.rs b/crates/proto-flow/src/materialize.rs index b3be5608d6e..4bc629a2acc 100644 --- a/crates/proto-flow/src/materialize.rs +++ b/crates/proto-flow/src/materialize.rs @@ -160,6 +160,13 @@ pub mod request { /// Last-persisted connector checkpoint state from a previous session. #[prost(bytes = "bytes", tag = "4")] pub state_json: ::prost::bytes::Bytes, + /// Sealed (encrypted) endpoint configuration of this materialization. + /// This is the SOPS-encrypted document from which the runtime derived the + /// decrypted `materialization.config_json`, including any `sops` metadata stanza. + /// Connectors may reuse it to emit `configUpdate`s that modify nonsensitive + /// overlay fields without re-encrypting the configuration. + #[prost(bytes = "bytes", tag = "5")] + pub sealed_config_json: ::prost::bytes::Bytes, } /// Load a document identified by its key. The given key may have never before been stored, /// but a given key will be sent in a transaction Load just one time. diff --git a/crates/proto-flow/src/materialize.serde.rs b/crates/proto-flow/src/materialize.serde.rs index 24be109e786..4b78b655139 100644 --- a/crates/proto-flow/src/materialize.serde.rs +++ b/crates/proto-flow/src/materialize.serde.rs @@ -1083,6 +1083,9 @@ impl serde::Serialize for request::Open { if !self.state_json.is_empty() { len += 1; } + if !self.sealed_config_json.is_empty() { + len += 1; + } let mut struct_ser = serializer.serialize_struct("materialize.Request.Open", len)?; if let Some(v) = self.materialization.as_ref() { struct_ser.serialize_field("materialization", v)?; @@ -1098,6 +1101,11 @@ impl serde::Serialize for request::Open { #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("state", &crate::as_raw_json(&self.state_json)?)?; } + if !self.sealed_config_json.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("sealedConfig", &crate::as_raw_json(&self.sealed_config_json)?)?; + } struct_ser.end() } } @@ -1113,6 +1121,8 @@ impl<'de> serde::Deserialize<'de> for request::Open { "range", "state_json", "state", + "sealed_config_json", + "sealedConfig", ]; #[allow(clippy::enum_variant_names)] @@ -1121,6 +1131,7 @@ impl<'de> serde::Deserialize<'de> for request::Open { Version, Range, StateJson, + SealedConfigJson, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -1147,6 +1158,7 @@ impl<'de> serde::Deserialize<'de> for request::Open { "version" => Ok(GeneratedField::Version), "range" => Ok(GeneratedField::Range), "state" | "state_json" => Ok(GeneratedField::StateJson), + "sealedConfig" | "sealed_config_json" => Ok(GeneratedField::SealedConfigJson), _ => Ok(GeneratedField::__SkipField__), } } @@ -1170,6 +1182,7 @@ impl<'de> serde::Deserialize<'de> for request::Open { let mut version__ = None; let mut range__ = None; let mut state_json__ = None; + let mut sealed_config_json__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Materialization => { @@ -1198,6 +1211,14 @@ impl<'de> serde::Deserialize<'de> for request::Open { Some(map_.next_value::()?.0) ; } + GeneratedField::SealedConfigJson => { + if sealed_config_json__.is_some() { + return Err(serde::de::Error::duplicate_field("sealedConfig")); + } + sealed_config_json__ = + Some(map_.next_value::()?.0) + ; + } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } @@ -1208,6 +1229,7 @@ impl<'de> serde::Deserialize<'de> for request::Open { version: version__.unwrap_or_default(), range: range__, state_json: state_json__.unwrap_or_default(), + sealed_config_json: sealed_config_json__.unwrap_or_default(), }) } } diff --git a/crates/proto-flow/tests/regression.rs b/crates/proto-flow/tests/regression.rs index 1ebfb97100b..52c43fe5e12 100644 --- a/crates/proto-flow/tests/regression.rs +++ b/crates/proto-flow/tests/regression.rs @@ -440,6 +440,9 @@ fn ex_capture_request() -> capture::Request { version: "11:22:33:44".to_string(), range: Some(ex_range()), state_json: json!({"connector": {"state": 42}}).to_string().into(), + sealed_config_json: json!({"encrypted": "c2VjcmV0", "sops": {"mac": "abc"}}) + .to_string() + .into(), }), acknowledge: Some(capture::request::Acknowledge { checkpoints: 32 }), internal: ex_internal(), @@ -625,6 +628,9 @@ fn ex_materialize_request() -> materialize::Request { version: "11:22:33:44".to_string(), range: Some(ex_range()), state_json: json!({"connector": {"state": 42}}).to_string().into(), + sealed_config_json: json!({"encrypted": "c2VjcmV0", "sops": {"mac": "abc"}}) + .to_string() + .into(), }), acknowledge: Some(materialize::request::Acknowledge { state_patches_json: json!([{"acked": true}]).to_string().into(), diff --git a/crates/proto-flow/tests/snapshots/regression__capture_request_json.snap b/crates/proto-flow/tests/snapshots/regression__capture_request_json.snap index 7fc6e48640c..ce2440328d5 100644 --- a/crates/proto-flow/tests/snapshots/regression__capture_request_json.snap +++ b/crates/proto-flow/tests/snapshots/regression__capture_request_json.snap @@ -425,7 +425,8 @@ expression: json_test(msg) "rClockBegin": 2291772091, "rClockEnd": 3437096703 }, - "state": {"connector":{"state":42}} + "state": {"connector":{"state":42}}, + "sealedConfig": {"encrypted":"c2VjcmV0","sops":{"mac":"abc"}} }, "acknowledge": { "checkpoints": 32 diff --git a/crates/proto-flow/tests/snapshots/regression__capture_request_proto.snap b/crates/proto-flow/tests/snapshots/regression__capture_request_proto.snap index 0f5edf2574e..79bf62c92cb 100644 --- a/crates/proto-flow/tests/snapshots/regression__capture_request_proto.snap +++ b/crates/proto-flow/tests/snapshots/regression__capture_request_proto.snap @@ -97,7 +97,7 @@ expression: proto_test(msg) |31313a32 323a3333 3a34342a 0b30303a| 11:22:33:44*.00: 000005c0 |31313a32 323a3333 321a7b22 636f6e6e| 11:22:332.{"conn 000005d0 |6563746f 72223a7b 22737461 7465223a| ector":{"state": 000005e0 -|34327d7d 2afa060a b8060a0e 61636d65| 42}}*.......acme 000005f0 +|34327d7d 2aa9070a b8060a0e 61636d65| 42}}*.......acme 000005f0 |436f2f63 61707475 72651007 1a197b22| Co/capture....{" 00000600 |63617074 75726522 3a7b2263 6f6e6669| capture":{"confi 00000610 |67223a34 327d7d22 a2040a15 7b227265| g":42}}"....{"re 00000620 @@ -153,5 +153,8 @@ expression: proto_test(msg) |14153322 11001d77 66554425 bbaa9988| ..3"...wfUD%.... 00000940 |2dffeedd cc221a7b 22636f6e 6e656374| -....".{"connect 00000950 |6f72223a 7b227374 61746522 3a34327d| or":{"state":42} 00000960 -|7d320208 20a20606 12024869 1801| }2.. .....Hi.. 00000970 - 0000097e +|7d2a2d7b 22656e63 72797074 6564223a| }*-{"encrypted": 00000970 +|22633256 6a636d56 30222c22 736f7073| "c2VjcmV0","sops 00000980 +|223a7b22 6d616322 3a226162 63227d7d| ":{"mac":"abc"}} 00000990 +|32020820 a2060612 02486918 01| 2.. .....Hi.. 000009a0 + 000009ad diff --git a/crates/proto-flow/tests/snapshots/regression__materialize_request_json.snap b/crates/proto-flow/tests/snapshots/regression__materialize_request_json.snap index 98421a66640..1399fa76b6f 100644 --- a/crates/proto-flow/tests/snapshots/regression__materialize_request_json.snap +++ b/crates/proto-flow/tests/snapshots/regression__materialize_request_json.snap @@ -515,7 +515,8 @@ expression: json_test(msg) "rClockBegin": 2291772091, "rClockEnd": 3437096703 }, - "state": {"connector":{"state":42}} + "state": {"connector":{"state":42}}, + "sealedConfig": {"encrypted":"c2VjcmV0","sops":{"mac":"abc"}} }, "load": { "binding": 12, diff --git a/crates/proto-flow/tests/snapshots/regression__materialize_request_proto.snap b/crates/proto-flow/tests/snapshots/regression__materialize_request_proto.snap index c60d34153c1..c8be91e5256 100644 --- a/crates/proto-flow/tests/snapshots/regression__materialize_request_proto.snap +++ b/crates/proto-flow/tests/snapshots/regression__materialize_request_proto.snap @@ -118,7 +118,7 @@ expression: proto_test(msg) |6562686f 6f6b227d 5d120b31 313a3232| ebhook"}]..11:22 00000710 |3a33333a 34342a0b 30303a31 313a3232| :33:44*.00:11:22 00000720 |3a333332 157b2263 6f6e6e65 63746f72| :332.{"connector 00000730 -|223a2273 74617465 227d22bd 090afb08| ":"state"}"..... 00000740 +|223a2273 74617465 227d22ec 090afb08| ":"state"}"..... 00000740 |0a166163 6d65436f 2f6d6174 65726961| ..acmeCo/materia 00000750 |6c697a61 74696f6e 10081a1d 7b226d61| lization....{"ma 00000760 |74657269 616c697a 65223a7b 22636f6e| terialize":{"con 00000770 @@ -194,22 +194,25 @@ expression: proto_test(msg) |32323a33 333a3434 1a141533 2211001d| 22:33:44...3"... 00000bd0 |77665544 25bbaa99 882dffee ddcc221a| wfUD%....-....". 00000be0 |7b22636f 6e6e6563 746f7222 3a7b2273| {"connector":{"s 00000bf0 -|74617465 223a3432 7d7d2a13 080c1209| tate":42}}*..... 00000c00 -|5b34322c 22686922 5d1a0456 4b1e0932| [42,"hi"]..VK..2 00000c10 -|110a0f5b 7b22666c 75736865 64223a31| ...[{"flushed":1 00000c20 -|7d5d3a45 0803120b 5b747275 652c6e75| }]:E....[true,nu 00000c30 -|6c6c5d1a 035a1500 22125b33 2e313431| ll]..Z..".[3.141 00000c40 -|35392c22 6669656c 6421225d 2a023c5b| 59,"field!"]*.<[ 00000c50 -|32137b22 66756c6c 223a2264 6f63756d| 2.{"full":"docum 00000c60 -|656e7422 7d380140 01427e0a 640a4a0a| ent"}8.@.B~.d.J. 00000c70 -|15612f72 6561642f 6a6f7572 6e616c3b| .a/read/journal; 00000c80 -|73756666 69781231 08b96012 150a0503| suffix.1..`..... 00000c90 -|09080507 120c09e3 21000000 00000010| ........!....... 00000ca0 -|d7081215 0a05070c 662b1d12 0c093501| ........f+....5. 00000cb0 -|00000000 000010ae 1112160a 0e616e2f| .............an/ 00000cc0 -|61636b2f 6a6f7572 6e616c12 04030402| ack/journal..... 00000cd0 -|0512165b 7b227374 61727465 64223a22| ...[{"started":" 00000ce0 -|636f6d6d 6974227d 5d4a120a 105b7b22| commit"}]J...[{" 00000cf0 -|61636b65 64223a74 7275657d 5da20606| acked":true}]... 00000d00 -|12024869 1801| ..Hi.. 00000d10 - 00000d16 +|74617465 223a3432 7d7d2a2d 7b22656e| tate":42}}*-{"en 00000c00 +|63727970 74656422 3a226332 566a636d| crypted":"c2Vjcm 00000c10 +|5630222c 22736f70 73223a7b 226d6163| V0","sops":{"mac 00000c20 +|223a2261 6263227d 7d2a1308 0c12095b| ":"abc"}}*.....[ 00000c30 +|34322c22 6869225d 1a04564b 1e093211| 42,"hi"]..VK..2. 00000c40 +|0a0f5b7b 22666c75 73686564 223a317d| ..[{"flushed":1} 00000c50 +|5d3a4508 03120b5b 74727565 2c6e756c| ]:E....[true,nul 00000c60 +|6c5d1a03 5a150022 125b332e 31343135| l]..Z..".[3.1415 00000c70 +|392c2266 69656c64 21225d2a 023c5b32| 9,"field!"]*.<[2 00000c80 +|137b2266 756c6c22 3a22646f 63756d65| .{"full":"docume 00000c90 +|6e74227d 38014001 427e0a64 0a4a0a15| nt"}8.@.B~.d.J.. 00000ca0 +|612f7265 61642f6a 6f75726e 616c3b73| a/read/journal;s 00000cb0 +|75666669 78123108 b9601215 0a050309| uffix.1..`...... 00000cc0 +|08050712 0c09e321 00000000 000010d7| .......!........ 00000cd0 +|0812150a 05070c66 2b1d120c 09350100| .......f+....5.. 00000ce0 +|00000000 0010ae11 12160a0e 616e2f61| ............an/a 00000cf0 +|636b2f6a 6f75726e 616c1204 03040205| ck/journal...... 00000d00 +|12165b7b 22737461 72746564 223a2263| ..[{"started":"c 00000d10 +|6f6d6d69 74227d5d 4a120a10 5b7b2261| ommit"}]J...[{"a 00000d20 +|636b6564 223a7472 75657d5d a2060612| cked":true}].... 00000d30 +|02486918 01| .Hi.. 00000d40 + 00000d45 diff --git a/crates/runtime-next/src/leader/capture/task.rs b/crates/runtime-next/src/leader/capture/task.rs index 4ece3a5fe17..871c4cb4adc 100644 --- a/crates/runtime-next/src/leader/capture/task.rs +++ b/crates/runtime-next/src/leader/capture/task.rs @@ -53,6 +53,7 @@ impl Task { capture: spec, range, state_json: _, + sealed_config_json: _, version, } = open.clone().open.context("expected Open")?; diff --git a/crates/runtime-next/src/shard/capture/connector.rs b/crates/runtime-next/src/shard/capture/connector.rs index 2b1a2b4faab..bd9dbad9bbd 100644 --- a/crates/runtime-next/src/shard/capture/connector.rs +++ b/crates/runtime-next/src/shard/capture/connector.rs @@ -20,7 +20,8 @@ pub async fn start( Option, Option, )> { - let (endpoint, config_json, connector_type, catalog_name) = extract_endpoint(&mut initial)?; + let (endpoint, config_json, connector_type, catalog_name, sealed_config_json) = + extract_endpoint(&mut initial)?; let (connector_tx, connector_rx) = mpsc::channel(crate::CHANNEL_BUFFER); fn start_rpc( @@ -37,12 +38,12 @@ pub async fn start( .boxed() } + // Sealed endpoint configuration, extracted from the matched endpoint and + // decrypted later, once the connector's spec response is available. + let sealed_config; let (mut connector_rx, container) = match endpoint { - models::CaptureEndpoint::Connector(models::ConnectorConfig { - image, - config: sealed_config, - }) => { - *config_json = unseal::decrypt_sops(&sealed_config).await?.into(); + models::CaptureEndpoint::Connector(models::ConnectorConfig { image, config }) => { + sealed_config = config; // Captures don't have conditional JSON fields, so _codec is unused. let (rx, container, _codec) = crate::image_connector::serve( image, @@ -66,16 +67,16 @@ pub async fn start( } models::CaptureEndpoint::Local(models::LocalConfig { command, - config: sealed_config, + config, env, protobuf, }) => { + sealed_config = config; let codec = if protobuf { connector_init::Codec::Proto } else { connector_init::Codec::Json }; - *config_json = unseal::decrypt_sops(&sealed_config).await?.into(); let rx = crate::local_connector::serve( command, @@ -104,6 +105,13 @@ pub async fn start( response => return Err(verify.fail_msg(response)), }; + // Decrypt the sealed endpoint configuration into the connector request, applying + // any nonsensitive `sops.overlay` properties subject to schema validation. + *config_json = + unseal::overlay::decrypt_with_overlay(&sealed_config, &spec_response.config_schema_json) + .await? + .into(); + let mut token_restart_at = None; if let Ok(Some(iam_config)) = iam_auth::extract_iam_auth_from_connector_config( config_json, @@ -123,6 +131,14 @@ pub async fn start( tokens.zeroize(); } } + + // Provide the connector with the sealed endpoint configuration alongside the + // decrypted `config_json`, so it may emit `configUpdate`s which adjust its own + // `sops.overlay` without re-encrypting the configuration. Only present on Open. + if let Some(sealed_config_json) = sealed_config_json { + *sealed_config_json = sealed_config.into(); + } + _ = connector_tx.try_send(initial); Ok((connector_tx, connector_rx, container, token_restart_at)) @@ -135,11 +151,12 @@ fn extract_endpoint<'r>( &'r mut bytes::Bytes, i32, Option, + Option<&'r mut bytes::Bytes>, )> { - let (connector_type, config_json, catalog_name) = match request { + let (connector_type, config_json, catalog_name, sealed_config_json) = match request { Request { spec: Some(spec), .. - } => (spec.connector_type, &mut spec.config_json, None), + } => (spec.connector_type, &mut spec.config_json, None, None), Request { discover: Some(discover), .. @@ -147,6 +164,7 @@ fn extract_endpoint<'r>( discover.connector_type, &mut discover.config_json, Some(discover.name.clone()), + None, ), Request { validate: Some(validate), @@ -155,6 +173,7 @@ fn extract_endpoint<'r>( validate.connector_type, &mut validate.config_json, Some(validate.name.clone()), + None, ), Request { apply: Some(apply), .. @@ -164,17 +183,28 @@ fn extract_endpoint<'r>( .capture .as_mut() .context("`apply` missing required `capture`")?; - (inner.connector_type, &mut inner.config_json, catalog_name) + ( + inner.connector_type, + &mut inner.config_json, + catalog_name, + None, + ) } Request { open: Some(open), .. } => { let catalog_name = open.capture.as_ref().map(|c| c.name.clone()); + let sealed_config_json = &mut open.sealed_config_json; let inner = open .capture .as_mut() .context("`open` missing required `capture`")?; - (inner.connector_type, &mut inner.config_json, catalog_name) + ( + inner.connector_type, + &mut inner.config_json, + catalog_name, + Some(sealed_config_json), + ) } request => { return Err( @@ -191,6 +221,7 @@ fn extract_endpoint<'r>( config_json, connector_type, catalog_name, + sealed_config_json, )) } else if connector_type == flow::capture_spec::ConnectorType::Local as i32 { Ok(( @@ -200,6 +231,7 @@ fn extract_endpoint<'r>( config_json, connector_type, catalog_name, + sealed_config_json, )) } else { anyhow::bail!("invalid connector type: {connector_type}"); diff --git a/crates/runtime-next/src/shard/capture/handler.rs b/crates/runtime-next/src/shard/capture/handler.rs index 20e2d7ba379..3eb9297f51a 100644 --- a/crates/runtime-next/src/shard/capture/handler.rs +++ b/crates/runtime-next/src/shard/capture/handler.rs @@ -335,6 +335,9 @@ where version: version.clone(), range: Some(range.clone()), state_json: connector_state_json, + // Populated by `connector::start` with the matched endpoint's inner + // sealed configuration, which is not yet extracted from `spec` here. + sealed_config_json: Default::default(), }), ..Default::default() }; diff --git a/crates/runtime-next/src/shard/materialize/connector.rs b/crates/runtime-next/src/shard/materialize/connector.rs index e2b8c971fe0..01f18b01ff7 100644 --- a/crates/runtime-next/src/shard/materialize/connector.rs +++ b/crates/runtime-next/src/shard/materialize/connector.rs @@ -23,7 +23,8 @@ pub async fn start( connector_init::Codec, Option, )> { - let (endpoint, config_json, connector_type, catalog_name) = extract_endpoint(&mut initial)?; + let (endpoint, config_json, connector_type, catalog_name, sealed_config_json) = + extract_endpoint(&mut initial)?; let (connector_tx, connector_rx) = mpsc::channel(crate::CHANNEL_BUFFER); fn start_rpc( @@ -40,12 +41,13 @@ pub async fn start( .boxed() } + // Sealed endpoint configuration, extracted from the matched endpoint and + // decrypted later, once the connector's spec response is available. `None` + // for Dekaf, which decrypts its own config outside this path. + let sealed_config; let (mut connector_rx, container, codec) = match endpoint { - models::MaterializationEndpoint::Connector(models::ConnectorConfig { - image, - config: sealed_config, - }) => { - *config_json = unseal::decrypt_sops(&sealed_config).await?.into(); + models::MaterializationEndpoint::Connector(models::ConnectorConfig { image, config }) => { + sealed_config = Some(config); let (rx, container, codec) = crate::image_connector::serve( image.clone(), @@ -72,16 +74,16 @@ pub async fn start( } models::MaterializationEndpoint::Local(models::LocalConfig { command, - config: sealed_config, + config, env, protobuf, }) => { + sealed_config = Some(config); let codec = if protobuf { connector_init::Codec::Proto } else { connector_init::Codec::Json }; - *config_json = unseal::decrypt_sops(&sealed_config).await?.into(); let rx = crate::local_connector::serve( command, @@ -96,7 +98,11 @@ pub async fn start( (rx, None, codec) } models::MaterializationEndpoint::Dekaf(_) => { - // Dekaf is in-process Rust and consumes prost requests directly. + // Dekaf is in-process Rust and consumes prost requests directly. It + // decrypts its own (nested) endpoint config, so there's nothing to + // decrypt or overlay here. + sealed_config = None; + let rx = dekaf_connector::connector(ReceiverStream::new(connector_rx)) .map_err(crate::anyhow_to_status) .boxed(); @@ -120,6 +126,15 @@ pub async fn start( response => return Err(verify.fail_msg(response)), }; + // Decrypt the sealed endpoint configuration into the connector request, applying + // any nonsensitive `sops.overlay` properties subject to schema validation. + if let Some(sealed_config) = &sealed_config { + *config_json = + unseal::overlay::decrypt_with_overlay(sealed_config, &spec_response.config_schema_json) + .await? + .into(); + } + let mut token_restart_at = None; if let Ok(Some(iam_config)) = iam_auth::extract_iam_auth_from_connector_config( config_json, @@ -140,6 +155,14 @@ pub async fn start( tokens.zeroize(); } } + + // Provide the connector with the sealed endpoint configuration alongside the + // decrypted `config_json`, so it may emit `configUpdate`s which adjust its own + // `sops.overlay` without re-encrypting the configuration. Only present on Open. + if let (Some(sealed_config_json), Some(sealed_config)) = (sealed_config_json, sealed_config) { + *sealed_config_json = sealed_config.into(); + } + _ = connector_tx.try_send(initial); Ok(( @@ -158,11 +181,12 @@ fn extract_endpoint<'r>( &'r mut bytes::Bytes, i32, Option, + Option<&'r mut bytes::Bytes>, )> { - let (connector_type, config_json, catalog_name) = match request { + let (connector_type, config_json, catalog_name, sealed_config_json) = match request { materialize::Request { spec: Some(spec), .. - } => (spec.connector_type, &mut spec.config_json, None), + } => (spec.connector_type, &mut spec.config_json, None, None), materialize::Request { validate: Some(validate), .. @@ -170,6 +194,7 @@ fn extract_endpoint<'r>( validate.connector_type, &mut validate.config_json, Some(validate.name.clone()), + None, ), materialize::Request { apply: Some(apply), .. @@ -180,18 +205,29 @@ fn extract_endpoint<'r>( .as_mut() .context("`apply` missing required `materialization`")?; - (inner.connector_type, &mut inner.config_json, catalog_name) + ( + inner.connector_type, + &mut inner.config_json, + catalog_name, + None, + ) } materialize::Request { open: Some(open), .. } => { let catalog_name = open.materialization.as_ref().map(|m| m.name.clone()); + let sealed_config_json = &mut open.sealed_config_json; let inner = open .materialization .as_mut() .context("`open` missing required `materialization`")?; - (inner.connector_type, &mut inner.config_json, catalog_name) + ( + inner.connector_type, + &mut inner.config_json, + catalog_name, + Some(sealed_config_json), + ) } request => { return Err( @@ -208,6 +244,7 @@ fn extract_endpoint<'r>( config_json, connector_type, catalog_name, + sealed_config_json, )) } else if connector_type == flow::materialization_spec::ConnectorType::Local as i32 { Ok(( @@ -217,6 +254,7 @@ fn extract_endpoint<'r>( config_json, connector_type, catalog_name, + sealed_config_json, )) } else if connector_type == flow::materialization_spec::ConnectorType::Dekaf as i32 { Ok(( @@ -226,6 +264,7 @@ fn extract_endpoint<'r>( config_json, connector_type, catalog_name, + sealed_config_json, )) } else { anyhow::bail!("invalid connector type: {connector_type}"); diff --git a/crates/runtime-next/src/shard/materialize/startup.rs b/crates/runtime-next/src/shard/materialize/startup.rs index f0aac87dd7b..310249bb5c7 100644 --- a/crates/runtime-next/src/shard/materialize/startup.rs +++ b/crates/runtime-next/src/shard/materialize/startup.rs @@ -242,6 +242,9 @@ where version, state_json: connector_state_json, range, + // Populated by `connector::start` with the matched endpoint's inner + // sealed configuration, which is not yet extracted from `spec` here. + sealed_config_json: Default::default(), }), ..Default::default() }; diff --git a/crates/runtime/src/capture/connector.rs b/crates/runtime/src/capture/connector.rs index 0dbb33bf731..759cc84e594 100644 --- a/crates/runtime/src/capture/connector.rs +++ b/crates/runtime/src/capture/connector.rs @@ -18,7 +18,8 @@ pub async fn start( BoxStream<'static, anyhow::Result>, )> { let log_level = initial.get_internal()?.log_level(); - let (endpoint, config_json, connector_type, catalog_name) = extract_endpoint(&mut initial)?; + let (endpoint, config_json, connector_type, catalog_name, sealed_config_json) = + extract_endpoint(&mut initial)?; let (mut connector_tx, connector_rx) = mpsc::channel(crate::CHANNEL_BUFFER); fn attach_container(response: &mut Response, container: crate::image_connector::Container) { @@ -41,12 +42,12 @@ pub async fn start( .boxed() } + // Sealed endpoint configuration, extracted from the matched endpoint and + // decrypted later, once the connector's spec response is available. + let sealed_config; let mut connector_rx = match endpoint { - models::CaptureEndpoint::Connector(models::ConnectorConfig { - image, - config: sealed_config, - }) => { - *config_json = unseal::decrypt_sops(&sealed_config).await?.into(); + models::CaptureEndpoint::Connector(models::ConnectorConfig { image, config }) => { + sealed_config = config; crate::image_connector::serve( attach_container, @@ -72,11 +73,11 @@ pub async fn start( } models::CaptureEndpoint::Local(models::LocalConfig { command, - config: sealed_config, + config, env, protobuf, }) => { - *config_json = unseal::decrypt_sops(&sealed_config).await?.into(); + sealed_config = config; crate::local_connector::serve( command, @@ -90,7 +91,7 @@ pub async fn start( } }; - // Send an initial Spec request which may direct us to perform an IAM token exchange. + // Send an initial Spec request connector_tx .try_send(Request { spec: Some(request::Spec { @@ -107,6 +108,14 @@ pub async fn start( response => return verify.fail(response), }; + // Decrypt the sealed endpoint configuration into the connector request, applying + // any nonsensitive `sops.overlay` properties subject to schema validation. + *config_json = + unseal::overlay::decrypt_with_overlay(&sealed_config, &spec_response.config_schema_json) + .await? + .into(); + + // The spec response may direct us to perform an IAM token exchange. if let Ok(Some(iam_config)) = iam_auth::extract_iam_auth_from_connector_config( config_json, &spec_response.config_schema_json, @@ -123,6 +132,13 @@ pub async fn start( } } + // Provide the connector with the sealed endpoint configuration alongside the + // decrypted `config_json`, so it may emit `configUpdate`s which adjust its own + // `sops.overlay` without re-encrypting the configuration. Only present on Open. + if let Some(sealed_config_json) = sealed_config_json { + *sealed_config_json = sealed_config.into(); + } + connector_tx.try_send(initial).unwrap(); Ok((connector_tx, connector_rx)) @@ -135,11 +151,12 @@ fn extract_endpoint<'r>( &'r mut bytes::Bytes, i32, Option, + Option<&'r mut bytes::Bytes>, )> { - let (connector_type, config_json, catalog_name) = match request { + let (connector_type, config_json, catalog_name, sealed_config_json) = match request { Request { spec: Some(spec), .. - } => (spec.connector_type, &mut spec.config_json, None), + } => (spec.connector_type, &mut spec.config_json, None, None), Request { discover: Some(discover), .. @@ -147,6 +164,7 @@ fn extract_endpoint<'r>( discover.connector_type, &mut discover.config_json, Some(discover.name.clone()), + None, ), Request { validate: Some(validate), @@ -155,6 +173,7 @@ fn extract_endpoint<'r>( validate.connector_type, &mut validate.config_json, Some(validate.name.clone()), + None, ), Request { apply: Some(apply), .. @@ -165,18 +184,29 @@ fn extract_endpoint<'r>( .as_mut() .context("`apply` missing required `capture`")?; - (inner.connector_type, &mut inner.config_json, catalog_name) + ( + inner.connector_type, + &mut inner.config_json, + catalog_name, + None, + ) } Request { open: Some(open), .. } => { let catalog_name = open.capture.as_ref().map(|c| c.name.clone()); + let sealed_config_json = &mut open.sealed_config_json; let inner = open .capture .as_mut() .context("`open` missing required `capture`")?; - (inner.connector_type, &mut inner.config_json, catalog_name) + ( + inner.connector_type, + &mut inner.config_json, + catalog_name, + Some(sealed_config_json), + ) } request => return verify("client", "valid first request").fail(request), }; @@ -189,6 +219,7 @@ fn extract_endpoint<'r>( config_json, connector_type, catalog_name, + sealed_config_json, )) } else if connector_type == ConnectorType::Local as i32 { Ok(( @@ -198,6 +229,7 @@ fn extract_endpoint<'r>( config_json, connector_type, catalog_name, + sealed_config_json, )) } else { anyhow::bail!("invalid connector type: {connector_type}"); diff --git a/crates/runtime/src/capture/task.rs b/crates/runtime/src/capture/task.rs index a452ef9149d..8f29be32871 100644 --- a/crates/runtime/src/capture/task.rs +++ b/crates/runtime/src/capture/task.rs @@ -10,6 +10,7 @@ impl Task { capture: spec, range, state_json: _, + sealed_config_json: _, version, } = open.clone().open.context("expected Open")?; diff --git a/crates/runtime/src/harness/capture.rs b/crates/runtime/src/harness/capture.rs index e75a645b750..704b0b75ef8 100644 --- a/crates/runtime/src/harness/capture.rs +++ b/crates/runtime/src/harness/capture.rs @@ -145,6 +145,7 @@ async fn run_session( r_clock_end: u32::MAX, }), state_json: std::mem::take(state).into(), + sealed_config_json: Default::default(), }), ..Default::default() } diff --git a/crates/runtime/src/harness/materialize.rs b/crates/runtime/src/harness/materialize.rs index bf7a6f6b3e7..5e0ddad4d52 100644 --- a/crates/runtime/src/harness/materialize.rs +++ b/crates/runtime/src/harness/materialize.rs @@ -139,6 +139,7 @@ async fn run_session( r_clock_end: u32::MAX, }), state_json: std::mem::take(state).into(), + sealed_config_json: Default::default(), }), ..Default::default() } diff --git a/crates/runtime/src/materialize/connector.rs b/crates/runtime/src/materialize/connector.rs index 3b7950ac5d9..f295a7a3648 100644 --- a/crates/runtime/src/materialize/connector.rs +++ b/crates/runtime/src/materialize/connector.rs @@ -29,7 +29,8 @@ pub async fn start( OpenExtras, )> { let log_level = initial.get_internal()?.log_level(); - let (endpoint, config_json, connector_type, catalog_name) = extract_endpoint(&mut initial)?; + let (endpoint, config_json, connector_type, catalog_name, sealed_config_json) = + extract_endpoint(&mut initial)?; let (mut connector_tx, connector_rx) = mpsc::channel(crate::CHANNEL_BUFFER); fn attach_container(response: &mut Response, container: crate::image_connector::Container) { @@ -52,12 +53,13 @@ pub async fn start( .boxed() } + // Sealed endpoint configuration, extracted from the matched endpoint and + // decrypted later, once the connector's spec response is available. `None` + // for Dekaf, which decrypts its own config outside this path. + let sealed_config; let (mut connector_rx, connector_image) = match endpoint { - models::MaterializationEndpoint::Connector(models::ConnectorConfig { - image, - config: sealed_config, - }) => { - *config_json = unseal::decrypt_sops(&sealed_config).await?.into(); + models::MaterializationEndpoint::Connector(models::ConnectorConfig { image, config }) => { + sealed_config = Some(config); let rx = crate::image_connector::serve( attach_container, @@ -87,11 +89,11 @@ pub async fn start( } models::MaterializationEndpoint::Local(models::LocalConfig { command, - config: sealed_config, + config, env, protobuf, }) => { - *config_json = unseal::decrypt_sops(&sealed_config).await?.into(); + sealed_config = Some(config); let rx = crate::local_connector::serve( command, @@ -105,10 +107,17 @@ pub async fn start( (rx, String::new()) } - models::MaterializationEndpoint::Dekaf(_) => ( - dekaf_connector::connector(connector_rx).boxed(), - String::new(), - ), + models::MaterializationEndpoint::Dekaf(_) => { + // Dekaf is in-process Rust and consumes prost requests directly. It + // decrypts its own (nested) endpoint config, so there's nothing to + // decrypt or overlay here. + sealed_config = None; + + ( + dekaf_connector::connector(connector_rx).boxed(), + String::new(), + ) + } }; // Send an initial Spec request which may direct us to perform an IAM token exchange. @@ -128,6 +137,15 @@ pub async fn start( response => return verify.fail(response), }; + // Decrypt the sealed endpoint configuration into the connector request, applying + // any nonsensitive `sops.overlay` properties subject to schema validation. + if let Some(sealed_config) = &sealed_config { + *config_json = + unseal::overlay::decrypt_with_overlay(sealed_config, &spec_response.config_schema_json) + .await? + .into(); + } + if let Ok(Some(iam_config)) = iam_auth::extract_iam_auth_from_connector_config( config_json, &spec_response.config_schema_json, @@ -144,6 +162,13 @@ pub async fn start( } } + // Provide the connector with the sealed endpoint configuration alongside the + // decrypted `config_json`, so it may emit `configUpdate`s which adjust its own + // `sops.overlay` without re-encrypting the configuration. Only present on Open. + if let (Some(sealed_config_json), Some(sealed_config)) = (sealed_config_json, sealed_config) { + *sealed_config_json = sealed_config.into(); + } + // Decrypt trigger configs and pre-compile their Handlebars templates. let triggers_json = initial .open @@ -199,11 +224,12 @@ fn extract_endpoint<'r>( &'r mut bytes::Bytes, i32, Option, + Option<&'r mut bytes::Bytes>, )> { - let (connector_type, config_json, catalog_name) = match request { + let (connector_type, config_json, catalog_name, sealed_config_json) = match request { Request { spec: Some(spec), .. - } => (spec.connector_type, &mut spec.config_json, None), + } => (spec.connector_type, &mut spec.config_json, None, None), Request { validate: Some(validate), .. @@ -211,6 +237,7 @@ fn extract_endpoint<'r>( validate.connector_type, &mut validate.config_json, Some(validate.name.clone()), + None, ), Request { apply: Some(apply), .. @@ -221,18 +248,29 @@ fn extract_endpoint<'r>( .as_mut() .context("`apply` missing required `materialization`")?; - (inner.connector_type, &mut inner.config_json, catalog_name) + ( + inner.connector_type, + &mut inner.config_json, + catalog_name, + None, + ) } Request { open: Some(open), .. } => { let catalog_name = open.materialization.as_ref().map(|m| m.name.clone()); + let sealed_config_json = &mut open.sealed_config_json; let inner = open .materialization .as_mut() .context("`open` missing required `materialization`")?; - (inner.connector_type, &mut inner.config_json, catalog_name) + ( + inner.connector_type, + &mut inner.config_json, + catalog_name, + Some(sealed_config_json), + ) } request => return crate::verify("client", "valid first request").fail(request), }; @@ -245,6 +283,7 @@ fn extract_endpoint<'r>( config_json, connector_type, catalog_name, + sealed_config_json, )) } else if connector_type == ConnectorType::Local as i32 { Ok(( @@ -254,6 +293,7 @@ fn extract_endpoint<'r>( config_json, connector_type, catalog_name, + sealed_config_json, )) } else if connector_type == ConnectorType::Dekaf as i32 { Ok(( @@ -263,6 +303,7 @@ fn extract_endpoint<'r>( config_json, connector_type, catalog_name, + sealed_config_json, )) } else { anyhow::bail!("invalid connector type: {connector_type}"); diff --git a/crates/runtime/src/materialize/task.rs b/crates/runtime/src/materialize/task.rs index 0c051069e34..3e6e375af87 100644 --- a/crates/runtime/src/materialize/task.rs +++ b/crates/runtime/src/materialize/task.rs @@ -9,6 +9,7 @@ impl Task { materialization: spec, range, state_json: _, + sealed_config_json: _, version, } = open.clone().open.context("expected Open")?; diff --git a/crates/unseal/Cargo.toml b/crates/unseal/Cargo.toml index d2794f671f0..4099bcd3b90 100644 --- a/crates/unseal/Cargo.toml +++ b/crates/unseal/Cargo.toml @@ -11,10 +11,13 @@ license.workspace = true [dependencies] async-process = { path = "../async-process" } bytes = { workspace = true } +doc = { path = "../doc" } +json = { path = "../json" } models = { path = "../models" } locate-bin = { path = "../locate-bin" } anyhow = { workspace = true } +json-patch = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } zeroize = { workspace = true } diff --git a/crates/unseal/src/lib.rs b/crates/unseal/src/lib.rs index 4e1483813d0..2024bccf6b5 100644 --- a/crates/unseal/src/lib.rs +++ b/crates/unseal/src/lib.rs @@ -1,6 +1,8 @@ use anyhow::Context; use zeroize::Zeroizing; +pub mod overlay; + /// Decrypt a `sops`-protected document using `sops` and application default credentials. pub async fn decrypt_sops(config: &models::RawValue) -> anyhow::Result { // Only objects can be `sops` documents. diff --git a/crates/unseal/src/overlay.rs b/crates/unseal/src/overlay.rs new file mode 100644 index 00000000000..447c6bfb244 --- /dev/null +++ b/crates/unseal/src/overlay.rs @@ -0,0 +1,248 @@ +use anyhow::Context; +use json::ptr::Token; + +/// Decrypt a `sops`-protected endpoint config, applying its `sops.overlay` if present. +/// +/// An endpoint config may carry a plaintext `sops.overlay` object within its `sops` +/// metadata stanza. The overlay mirrors the structure of the config and is merged into +/// the SOPS-decrypted document using JSON Merge Patch (RFC 7396) semantics, which lets +/// non-security-relevant fields be modified without re-encrypting (and re-MAC-ing) the +/// configuration. +/// +/// Before merging, the overlay is validated against `schema` to ensure it only modifies +/// locations annotated `nonsensitive: true`. This is what prevents an overlay from, say, +/// rewriting a database hostname out from under an encrypted password. +/// +/// When the config has no overlay this is byte-for-byte equivalent to [`super::decrypt_sops`]. +pub async fn decrypt_with_overlay( + sealed: &models::RawValue, + schema: &[u8], +) -> anyhow::Result { + let Some(overlay) = extract_overlay(sealed)? else { + return super::decrypt_sops(sealed).await; + }; + + validate_overlay_nonsensitive(&overlay, schema) + .context("validating endpoint config `sops.overlay`")?; + + let decrypted = super::decrypt_sops(sealed).await?; + + // Merge the overlay into the decrypted base via JSON Merge Patch (RFC 7396). + let mut merged = decrypted.to_value(); + json_patch::merge(&mut merged, &overlay); + + Ok(models::RawValue::from_value(&merged)) +} + +/// Pull the `sops.overlay` object out of a sealed config, if one is present. +/// A missing or `null` overlay returns `None`, which is the backwards-compatible +/// no-op case shared by all configs encrypted before overlays existed. +fn extract_overlay(sealed: &models::RawValue) -> anyhow::Result> { + let dom = sealed.to_value(); + + let overlay = dom + .as_object() + .and_then(|doc| doc.get("sops")) + .and_then(|sops| sops.as_object()) + .and_then(|sops| sops.get("overlay")); + + match overlay { + None | Some(serde_json::Value::Null) => Ok(None), + Some(overlay) => Ok(Some(overlay.clone())), + } +} + +/// Validate that every location an `overlay` would modify under JSON Merge Patch semantics +/// is annotated `nonsensitive: true` in `schema`, or lies within a `nonsensitive` subtree. +/// +/// A `nonsensitive` annotation is a human assertion that everything within is safe to modify +/// outside the SOPS MAC. This is the sole protection, so the annotations must be vetted by a +/// human and should err on the side of safety when it's unclear if a location is sensitive. +fn validate_overlay_nonsensitive(overlay: &serde_json::Value, schema: &[u8]) -> anyhow::Result<()> { + let bundle = doc::validation::build_bundle(schema).context("building config schema")?; + let validator = doc::Validator::new(bundle).context("preparing config schema validator")?; + let shape = doc::Shape::infer(validator.schema(), validator.schema_index()); + + check_location(&shape, &mut json::Pointer(Vec::new()), overlay) +} + +fn check_location( + root: &doc::Shape, + ptr: &mut json::Pointer, + node: &serde_json::Value, +) -> anyhow::Result<()> { + let (located, _exists) = root.locate(ptr); + + // A nonsensitive annotation authorizes this location and/or subtree. + if located.nonsensitive == Some(true) { + return Ok(()); + } + + let fields = match node { + // A non-empty object recurses: each member is itself a write, checked in turn. + serde_json::Value::Object(fields) if !fields.is_empty() => fields, + // An empty object looks inert but is not. Under JSON Merge Patch a non-object + // base is first replaced by `{}` before members are merged (RFC 7396), so an + // empty object could still silently clobber a scalar or array secret it lands + // on. An empty object is safe IFF the schema requires that location to be an + // object in the target. + serde_json::Value::Object(_) if located.type_ == json::schema::types::OBJECT => { + return Ok(()); + } + // A scalar, array, `null`, or clobbering empty object is a merge-patch write + // at a location which, per the check above, is not marked nonsensitive. + _ => { + anyhow::bail!("overlay modifies location '{ptr}', which is not marked nonsensitive") + } + }; + + for (property, child) in fields { + ptr.push(Token::Property(property.clone())); + check_location(root, ptr, child)?; + ptr.0.pop(); + } + Ok(()) +} + +#[cfg(test)] +mod test { + use super::{extract_overlay, validate_overlay_nonsensitive}; + use serde_json::json; + + // A source-postgres-esque schema: `address`/`credentials` are sensitive (unannotated), + // `/advanced/backfill_chunk_size` is a single nonsensitive leaf, and `/tunables` is a + // nonsensitive subtree. + fn schema() -> Vec { + json!({ + "type": "object", + "properties": { + "address": { "type": "string" }, + "credentials": { + "type": "object", + "properties": { + "user": { "type": "string" }, + "password": { "type": "string", "secret": true }, + }, + }, + "advanced": { + "type": "object", + "properties": { + "backfill_chunk_size": { "type": "integer", "nonsensitive": true }, + "slot_name": { "type": "string" }, + }, + }, + "tunables": { + "type": "object", + "nonsensitive": true, + "properties": { + "parallelism": { "type": "integer" }, + }, + }, + }, + }) + .to_string() + .into_bytes() + } + + fn check(overlay: serde_json::Value) -> anyhow::Result<()> { + validate_overlay_nonsensitive(&overlay, &schema()) + } + + #[test] + fn nonsensitive_leaf_is_accepted() { + check(json!({"advanced": {"backfill_chunk_size": 50000}})).unwrap(); + } + + #[test] + fn nonsensitive_subtree_is_accepted() { + // Anything within a `nonsensitive` subtree is permitted, including new + // properties and nested objects the schema does not enumerate. + check(json!({"tunables": {"parallelism": 8, "extra": {"deep": true}}})).unwrap(); + } + + #[test] + fn empty_overlay_is_a_noop() { + // A whole-config empty overlay, and an empty object over the object-typed + // (and thus never-clobbered) `credentials` location, are both true no-ops. + check(json!({})).unwrap(); + check(json!({"credentials": {}})).unwrap(); + } + + #[test] + fn empty_object_clobbering_a_scalar_is_rejected() { + // `address` is a sensitive string. An empty object is not the no-op it looks + // like: merge-patch would reset the string to `{}`, dropping the secret, so it + // must be rejected the same as any other write to a sensitive location. + let err = check(json!({"address": {}})).unwrap_err(); + assert!(err.to_string().contains("/address"), "{err}"); + } + + #[test] + fn sensitive_scalar_is_rejected() { + // The hostname-rewrite attack: not nonsensitive, so it cannot be overlaid. + let err = check(json!({"address": "evil.example.com:5432"})).unwrap_err(); + assert!(err.to_string().contains("/address"), "{err}"); + } + + #[test] + fn nested_sensitive_field_is_rejected() { + // A field nested within an unannotated object is sensitive like any other. + let err = check(json!({"credentials": {"password": "pwn"}})).unwrap_err(); + assert!(err.to_string().contains("/credentials/password"), "{err}"); + } + + #[test] + fn null_delete_of_sensitive_field_is_rejected() { + // A merge-patch `null` deletes a key, which is still a modification. + assert!(check(json!({"address": null})).is_err()); + assert!(check(json!({"advanced": {"slot_name": null}})).is_err()); + } + + #[test] + fn sensitive_sibling_within_advanced_is_rejected() { + // `/advanced` itself is not nonsensitive: only `backfill_chunk_size` is. + assert!(check(json!({"advanced": {"slot_name": "flow_slot"}})).is_err()); + } + + #[test] + fn unknown_field_is_rejected() { + // A field absent from the schema cannot be proven nonsensitive. + assert!(check(json!({"advanced": {"not_in_schema": 1}})).is_err()); + assert!(check(json!({"totally_unknown": "x"})).is_err()); + } + + #[test] + fn non_object_root_overlay_is_rejected() { + assert!(check(json!("just a string")).is_err()); + assert!(check(json!([1, 2, 3])).is_err()); + } + + #[test] + fn unparseable_schema_rejects_overlay() { + // Fail-safe: without a usable schema we cannot prove anything nonsensitive. + assert!( + validate_overlay_nonsensitive(&json!({"advanced": {"backfill_chunk_size": 1}}), b"") + .is_err() + ); + } + + #[test] + fn extract_overlay_variants() { + let absent: Box = + serde_json::from_value(json!({"address": "db:5432"})).unwrap(); + assert!(extract_overlay(&absent).unwrap().is_none()); + + let null: Box = + serde_json::from_value(json!({"sops": {"overlay": null}})).unwrap(); + assert!(extract_overlay(&null).unwrap().is_none()); + + let present: Box = serde_json::from_value( + json!({"sops": {"overlay": {"advanced": {"backfill_chunk_size": 50000}}}}), + ) + .unwrap(); + assert_eq!( + extract_overlay(&present).unwrap().unwrap(), + json!({"advanced": {"backfill_chunk_size": 50000}}), + ); + } +} diff --git a/go/protocols/capture/capture.pb.go b/go/protocols/capture/capture.pb.go index d8258d87bb7..1fe145a40a0 100644 --- a/go/protocols/capture/capture.pb.go +++ b/go/protocols/capture/capture.pb.go @@ -348,7 +348,13 @@ type Request_Open struct { // Range of documents to be processed by this invocation. Range *flow.RangeSpec `protobuf:"bytes,3,opt,name=range,proto3" json:"range,omitempty"` // Last-persisted connector checkpoint state from a previous invocation. - StateJson encoding_json.RawMessage `protobuf:"bytes,4,opt,name=state_json,json=state,proto3,casttype=encoding/json.RawMessage" json:"state_json,omitempty"` + StateJson encoding_json.RawMessage `protobuf:"bytes,4,opt,name=state_json,json=state,proto3,casttype=encoding/json.RawMessage" json:"state_json,omitempty"` + // Sealed (encrypted) endpoint configuration of this capture. + // This is the SOPS-encrypted document from which the runtime derived the + // decrypted `capture.config_json`, including any `sops` metadata stanza. + // Connectors may reuse it to emit `configUpdate`s that modify nonsensitive + // overlay fields without re-encrypting the configuration. + SealedConfigJson encoding_json.RawMessage `protobuf:"bytes,5,opt,name=sealed_config_json,json=sealedConfig,proto3,casttype=encoding/json.RawMessage" json:"sealed_config_json,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1020,86 +1026,87 @@ func init() { } var fileDescriptor_841a70e6e6288f13 = []byte{ - // 1251 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0x4d, 0x6f, 0x1b, 0xc5, - 0x1b, 0xef, 0xfa, 0x75, 0xfd, 0xd8, 0x4e, 0x93, 0x51, 0xfe, 0xd5, 0x76, 0x1b, 0x25, 0xe9, 0xcb, - 0x1f, 0xa5, 0x2f, 0xd8, 0xc5, 0x6d, 0x81, 0xf2, 0xd2, 0x92, 0xa4, 0x54, 0xa2, 0x08, 0x5a, 0x4d, - 0xa1, 0x08, 0x2e, 0xab, 0xcd, 0xec, 0xc4, 0x5e, 0xb2, 0xde, 0x59, 0x76, 0xd7, 0x2d, 0x96, 0xb8, - 0x23, 0xbe, 0x00, 0xe7, 0x9e, 0xb8, 0xf1, 0x31, 0x90, 0x72, 0xe0, 0xc0, 0x91, 0x53, 0x04, 0xe5, - 0x5b, 0xf4, 0x84, 0xe6, 0x6d, 0xbd, 0xf6, 0x26, 0xc1, 0xad, 0x8a, 0xc4, 0x25, 0xf1, 0xcc, 0xf3, - 0x7b, 0x66, 0x9f, 0xb7, 0xf9, 0xfd, 0x06, 0xce, 0xf5, 0x59, 0x37, 0x8a, 0x59, 0xca, 0x08, 0x0b, - 0x92, 0x2e, 0x71, 0xa3, 0x74, 0x14, 0x53, 0xfd, 0xbf, 0x23, 0x2c, 0xa8, 0xae, 0x96, 0xf6, 0xca, - 0x14, 0x78, 0x37, 0x60, 0x4f, 0xc4, 0x1f, 0x09, 0xb3, 0x97, 0xfb, 0xac, 0xcf, 0xc4, 0xcf, 0x2e, - 0xff, 0x25, 0x77, 0xcf, 0xfd, 0xd4, 0x86, 0x3a, 0xa6, 0xdf, 0x8c, 0x68, 0x92, 0xa2, 0x8b, 0x50, - 0x49, 0x22, 0x4a, 0x2c, 0x63, 0xdd, 0xd8, 0x68, 0xf6, 0xfe, 0xd7, 0xd1, 0x9f, 0x51, 0xf6, 0xce, - 0xc3, 0x88, 0x12, 0x2c, 0x20, 0xe8, 0x06, 0x98, 0x9e, 0x9f, 0x10, 0xf6, 0x98, 0xc6, 0x56, 0x49, - 0xc0, 0x4f, 0x17, 0xe0, 0x77, 0x14, 0x00, 0x67, 0x50, 0xee, 0xf6, 0xd8, 0x0d, 0x7c, 0xcf, 0x4d, - 0xa9, 0x55, 0x3e, 0xc2, 0xed, 0x91, 0x02, 0xe0, 0x0c, 0x8a, 0xae, 0x40, 0xd5, 0x8d, 0xa2, 0x60, - 0x6c, 0x55, 0x84, 0xcf, 0xa9, 0x82, 0xcf, 0x26, 0xb7, 0x62, 0x09, 0xe2, 0x69, 0xb0, 0x88, 0x86, - 0x56, 0xf5, 0x88, 0x34, 0xee, 0x47, 0x34, 0xc4, 0x02, 0x82, 0x6e, 0x41, 0xd3, 0x25, 0x7b, 0x21, - 0x7b, 0x12, 0x50, 0xaf, 0x4f, 0xad, 0x9a, 0xf0, 0x58, 0x29, 0x1e, 0x3f, 0xc1, 0xe0, 0xbc, 0x03, - 0x3a, 0x03, 0xa6, 0x1f, 0xa6, 0x34, 0x0e, 0xdd, 0xc0, 0xf2, 0xd6, 0x8d, 0x8d, 0x16, 0x6e, 0x5c, - 0xd0, 0x1b, 0xf6, 0x0f, 0x06, 0x54, 0x78, 0xc9, 0xd0, 0x5d, 0x58, 0x20, 0x2c, 0x0c, 0x29, 0x49, - 0x59, 0xec, 0xa4, 0xe3, 0x88, 0x8a, 0x0a, 0x2f, 0xf4, 0xd6, 0x3a, 0xa2, 0x3d, 0xdb, 0xf2, 0x6b, - 0x1c, 0xda, 0xd9, 0xd6, 0xb8, 0xcf, 0xc6, 0x11, 0xc5, 0x6d, 0x92, 0x5f, 0xa2, 0x9b, 0xd0, 0x24, - 0x2c, 0xdc, 0xf5, 0xfb, 0xce, 0xd7, 0x09, 0x0b, 0x45, 0xdd, 0x5b, 0x5b, 0x2b, 0xcf, 0x0f, 0xd6, - 0x2c, 0x1a, 0x12, 0xe6, 0xf9, 0x61, 0xbf, 0xcb, 0x0d, 0x1d, 0xec, 0x3e, 0xf9, 0x84, 0x26, 0x89, - 0xdb, 0xa7, 0xb8, 0x26, 0x1d, 0xec, 0xdf, 0x0d, 0x30, 0x75, 0x3f, 0xd0, 0x47, 0x50, 0x09, 0xdd, - 0xa1, 0xec, 0x40, 0x63, 0xeb, 0xc6, 0xf3, 0x83, 0xb5, 0x37, 0xfa, 0x7e, 0x3a, 0x18, 0xed, 0x74, - 0x08, 0x1b, 0x76, 0x69, 0x92, 0x8e, 0xdc, 0x78, 0x2c, 0xe7, 0xa7, 0x30, 0x51, 0x3a, 0x5a, 0x2c, - 0x8e, 0xf8, 0x2f, 0xa4, 0xf6, 0xb4, 0x02, 0xa6, 0x9e, 0x99, 0x2c, 0x35, 0xe3, 0xdf, 0x48, 0xad, - 0xf4, 0x2a, 0x52, 0x2b, 0xcf, 0x9f, 0x1a, 0x7a, 0x1f, 0xcc, 0x1d, 0x3f, 0xe4, 0x90, 0xc4, 0xaa, - 0xac, 0x97, 0x37, 0x9a, 0xbd, 0xb3, 0x47, 0x5e, 0x97, 0xce, 0x96, 0x44, 0xe2, 0xcc, 0x05, 0x5d, - 0x87, 0x56, 0xe0, 0x26, 0xa9, 0xa3, 0x5c, 0xd4, 0x85, 0x58, 0x2a, 0xc4, 0x8f, 0x9b, 0x1c, 0xa6, - 0x36, 0xd0, 0x59, 0xe5, 0xf5, 0x98, 0xc6, 0x89, 0xcf, 0x42, 0x71, 0x29, 0x1a, 0x12, 0xf2, 0x48, - 0x6e, 0xd9, 0x3f, 0x1b, 0x50, 0x57, 0x9f, 0x43, 0xf7, 0x60, 0x39, 0xa6, 0x09, 0x1b, 0xc5, 0x84, - 0x3a, 0xf9, 0x3c, 0x8d, 0x39, 0xf2, 0x5c, 0xd0, 0x9e, 0xdb, 0x32, 0xdf, 0x77, 0x00, 0x08, 0x0b, - 0x02, 0x4a, 0x52, 0x5f, 0x0d, 0x41, 0xb3, 0xb7, 0xac, 0xc2, 0xcd, 0xf6, 0x79, 0xc4, 0x5b, 0x95, - 0xfd, 0x83, 0xb5, 0x13, 0x38, 0x87, 0x46, 0x36, 0x98, 0x3b, 0x2e, 0xd9, 0xdb, 0xf5, 0x83, 0x40, - 0xd4, 0xb8, 0x8d, 0xb3, 0xb5, 0xfd, 0x87, 0x01, 0x55, 0x41, 0x11, 0xe8, 0x32, 0x68, 0xb6, 0x54, - 0x2c, 0x77, 0x48, 0x35, 0x34, 0x02, 0x59, 0x50, 0xd7, 0x45, 0x28, 0x89, 0x22, 0xe8, 0x65, 0xa1, - 0xb2, 0x95, 0x97, 0xaa, 0x6c, 0xb5, 0x50, 0x59, 0xf4, 0x16, 0x40, 0x92, 0xba, 0x29, 0x95, 0x35, - 0xac, 0xcd, 0x51, 0xc3, 0xaa, 0xc0, 0xf3, 0x96, 0x54, 0x38, 0xb1, 0xbd, 0xaa, 0x0c, 0xff, 0x0f, - 0xd5, 0xd8, 0x0d, 0xfb, 0x9a, 0xa6, 0x4f, 0xca, 0x43, 0x30, 0xdf, 0x12, 0x47, 0x48, 0xeb, 0x4c, - 0xbc, 0x95, 0xf9, 0xe3, 0xed, 0x42, 0x33, 0xc7, 0xaa, 0x68, 0x1d, 0x9a, 0x64, 0x40, 0xc9, 0x5e, - 0xc4, 0xfc, 0x30, 0x4d, 0x44, 0xe4, 0x6d, 0x9c, 0xdf, 0x3a, 0xf7, 0xfd, 0x02, 0x98, 0x98, 0x26, - 0x11, 0x0b, 0x13, 0x8a, 0x2e, 0x4d, 0x29, 0x55, 0x5e, 0x0f, 0x24, 0x20, 0x2f, 0x55, 0xef, 0x01, - 0x68, 0xfd, 0xa1, 0x9e, 0x1a, 0xaa, 0x95, 0xa2, 0xc7, 0x9d, 0x0c, 0x83, 0x73, 0x78, 0x74, 0x13, - 0x1a, 0x5a, 0x86, 0x3c, 0x55, 0x8b, 0x33, 0x45, 0x67, 0x7d, 0x09, 0x3d, 0x3c, 0x41, 0xa3, 0x6b, - 0x50, 0xe7, 0x82, 0xe4, 0x53, 0x4f, 0xcd, 0xc7, 0xe9, 0xa2, 0xe3, 0xa6, 0x04, 0x60, 0x8d, 0x44, - 0x57, 0xa1, 0xc6, 0x95, 0x89, 0x7a, 0xea, 0xb6, 0x5a, 0x45, 0x9f, 0xfb, 0xc2, 0x8e, 0x15, 0x0e, - 0xbd, 0x09, 0xa6, 0x82, 0x78, 0x4a, 0xc0, 0xec, 0xa2, 0x8f, 0xea, 0xbe, 0x87, 0x33, 0x2c, 0xe7, - 0x37, 0x79, 0xf9, 0x3c, 0x27, 0x21, 0x03, 0x3a, 0x74, 0x2d, 0x53, 0x78, 0xaf, 0x1d, 0x52, 0x4d, - 0x89, 0x7b, 0x28, 0x60, 0xb8, 0x9d, 0xe4, 0x97, 0xbc, 0xbe, 0x93, 0x3e, 0x59, 0xf5, 0xa3, 0xea, - 0xbb, 0x9d, 0x61, 0x70, 0x0e, 0x7f, 0xbc, 0x82, 0xfe, 0x5a, 0x52, 0x0a, 0x6a, 0x83, 0xa9, 0xb9, - 0x5a, 0xcd, 0x46, 0xb6, 0x46, 0x77, 0x01, 0x29, 0xde, 0x91, 0x69, 0xcc, 0xaf, 0x20, 0x2d, 0xe9, - 0xa7, 0xf2, 0xf8, 0x02, 0xce, 0xcc, 0x12, 0x59, 0xfe, 0xc0, 0x79, 0x78, 0x7b, 0x79, 0x9a, 0xcf, - 0xd4, 0xc1, 0x97, 0x61, 0xc9, 0x63, 0x64, 0x34, 0xa4, 0x61, 0xea, 0x72, 0xaa, 0x72, 0x46, 0x71, - 0x20, 0x26, 0xa2, 0x81, 0x17, 0xa7, 0x0c, 0x9f, 0xc7, 0x01, 0xba, 0x00, 0x35, 0xe6, 0x8e, 0xd2, - 0x41, 0x4f, 0xf5, 0xbf, 0x25, 0x2f, 0xde, 0xfd, 0x4d, 0xbe, 0x87, 0x95, 0x0d, 0x5d, 0x87, 0x53, - 0x59, 0xac, 0x91, 0x9b, 0x0e, 0x1c, 0x51, 0x4c, 0x1a, 0x27, 0x56, 0x6d, 0xbd, 0xbc, 0xd1, 0x98, - 0x04, 0xf2, 0xc0, 0x4d, 0x07, 0x0f, 0x94, 0xcd, 0xfe, 0xb1, 0x0c, 0x30, 0x19, 0x73, 0xf4, 0x41, - 0x4e, 0x5d, 0x0c, 0xa1, 0x2e, 0x17, 0x8e, 0xbb, 0x16, 0x45, 0x81, 0xb1, 0x7f, 0x29, 0x4d, 0x74, - 0xe0, 0x22, 0x2c, 0xc6, 0x94, 0xb0, 0xe1, 0x90, 0x86, 0x1e, 0xf5, 0x9c, 0x89, 0x0a, 0xe3, 0x93, - 0xb9, 0xfd, 0x4f, 0xb9, 0xb2, 0x1e, 0x25, 0x19, 0xa5, 0x97, 0x90, 0x8c, 0x7b, 0xb0, 0xac, 0x6b, - 0xf8, 0xc2, 0xed, 0x5a, 0xd0, 0x9e, 0xaa, 0x51, 0x8b, 0x50, 0xde, 0xa3, 0x63, 0xa1, 0xb4, 0x0d, - 0xcc, 0x7f, 0x72, 0x7e, 0xf4, 0xfc, 0xc4, 0xdd, 0x09, 0xa4, 0x78, 0x9a, 0x58, 0x2f, 0xd1, 0x79, - 0x68, 0x4f, 0x75, 0x40, 0x15, 0xbe, 0x95, 0x2f, 0x3c, 0x7a, 0x0d, 0x4e, 0xfa, 0x89, 0xb3, 0xeb, - 0x06, 0x01, 0x97, 0x22, 0x87, 0x1f, 0x5e, 0x17, 0xc7, 0xb4, 0xfd, 0xe4, 0xae, 0xda, 0xfd, 0x98, - 0x8e, 0xed, 0xef, 0xa0, 0x91, 0x31, 0x08, 0xba, 0x5d, 0x68, 0xcb, 0xf9, 0x63, 0x08, 0xe7, 0x90, - 0xae, 0x74, 0x26, 0x4d, 0x29, 0x44, 0x69, 0x14, 0xa3, 0xb4, 0x3d, 0xa8, 0x2b, 0x1a, 0x42, 0xaf, - 0x03, 0x72, 0x85, 0x9c, 0x3a, 0x1e, 0x4d, 0x48, 0xec, 0x47, 0x42, 0x88, 0x65, 0x1b, 0x97, 0xa4, - 0xe5, 0xce, 0xc4, 0x80, 0x2e, 0x81, 0x64, 0xf3, 0x59, 0xa9, 0x56, 0xcf, 0x9f, 0x87, 0xdc, 0xa6, - 0x09, 0xff, 0x43, 0xa8, 0x49, 0xe2, 0x42, 0xef, 0xc2, 0x69, 0xfa, 0x6d, 0x14, 0xf8, 0xc4, 0x4f, - 0x9d, 0xdc, 0x63, 0x9a, 0x37, 0x42, 0x32, 0xbf, 0x89, 0x2d, 0x0d, 0xd8, 0x9c, 0xb1, 0xdb, 0x5f, - 0x82, 0xa9, 0xb9, 0x8c, 0x77, 0x47, 0x25, 0xad, 0x48, 0x41, 0x2f, 0xd1, 0x35, 0x30, 0x3d, 0x46, - 0xe6, 0x9f, 0xaa, 0xb2, 0xc7, 0x88, 0x1d, 0x40, 0x7b, 0x8a, 0xe8, 0x8e, 0x39, 0x7f, 0x13, 0x9a, - 0x2f, 0x4a, 0x36, 0x33, 0xc3, 0x66, 0xbf, 0x0d, 0x30, 0xa1, 0xc4, 0x49, 0x25, 0x8d, 0x7f, 0xac, - 0x64, 0xef, 0x36, 0x34, 0x32, 0x03, 0xea, 0x41, 0x5d, 0x3f, 0x2f, 0x16, 0x67, 0xdf, 0x86, 0xf6, - 0x52, 0x61, 0x70, 0x36, 0x8c, 0xab, 0xc6, 0xd6, 0xad, 0xfd, 0x3f, 0x57, 0x4f, 0xec, 0x3f, 0x5b, - 0x35, 0x7e, 0x7b, 0xb6, 0x6a, 0x3c, 0xfd, 0x6b, 0xd5, 0xf8, 0xea, 0xca, 0x5c, 0x8f, 0x64, 0x75, - 0xd8, 0x4e, 0x4d, 0x6c, 0x5d, 0xfb, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xb3, 0xb5, 0xd3, 0xc9, 0x9d, - 0x0e, 0x00, 0x00, + // 1271 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0xcb, 0x6f, 0xdc, 0x44, + 0x18, 0xaf, 0xb3, 0x2f, 0xef, 0xb7, 0xbb, 0x69, 0x32, 0x0a, 0x95, 0xeb, 0x46, 0x49, 0xfa, 0x00, + 0xa5, 0x0f, 0x36, 0x65, 0xdb, 0x02, 0xe5, 0xd1, 0x92, 0xa4, 0x54, 0xa2, 0x08, 0x5a, 0x4d, 0xa1, + 0x08, 0x2e, 0x96, 0x33, 0x9e, 0x6c, 0x4c, 0xbc, 0x1e, 0xe3, 0xf1, 0xb6, 0xac, 0xc4, 0x1d, 0xf1, + 0x0f, 0x70, 0xee, 0x3f, 0xc0, 0x9f, 0x81, 0xd4, 0x03, 0x07, 0x8e, 0x9c, 0x2a, 0x28, 0x77, 0x6e, + 0x5c, 0x7a, 0x42, 0xf3, 0xf2, 0x7a, 0xd7, 0x4d, 0xd8, 0x56, 0x45, 0xe2, 0x92, 0xec, 0xcc, 0xf7, + 0xfb, 0x3e, 0xcf, 0xf7, 0x98, 0xdf, 0x6f, 0xe0, 0x54, 0x9f, 0x6d, 0x24, 0x29, 0xcb, 0x18, 0x61, + 0x11, 0xdf, 0x20, 0x7e, 0x92, 0x0d, 0x53, 0x6a, 0xfe, 0x77, 0xa5, 0x05, 0x35, 0xf4, 0xd2, 0x5d, + 0x9e, 0x00, 0xef, 0x46, 0xec, 0x81, 0xfc, 0xa3, 0x60, 0xee, 0x52, 0x9f, 0xf5, 0x99, 0xfc, 0xb9, + 0x21, 0x7e, 0xa9, 0xdd, 0x53, 0x7f, 0x75, 0xa0, 0x81, 0xe9, 0x37, 0x43, 0xca, 0x33, 0x74, 0x16, + 0xaa, 0x3c, 0xa1, 0xc4, 0xb1, 0xd6, 0xac, 0xf5, 0x56, 0xef, 0x95, 0xae, 0xf9, 0x8c, 0xb6, 0x77, + 0xef, 0x26, 0x94, 0x60, 0x09, 0x41, 0x57, 0xc0, 0x0e, 0x42, 0x4e, 0xd8, 0x7d, 0x9a, 0x3a, 0x73, + 0x12, 0x7e, 0xbc, 0x04, 0xbf, 0xa1, 0x01, 0x38, 0x87, 0x0a, 0xb7, 0xfb, 0x7e, 0x14, 0x06, 0x7e, + 0x46, 0x9d, 0xca, 0x01, 0x6e, 0xf7, 0x34, 0x00, 0xe7, 0x50, 0x74, 0x01, 0x6a, 0x7e, 0x92, 0x44, + 0x23, 0xa7, 0x2a, 0x7d, 0x8e, 0x95, 0x7c, 0x36, 0x85, 0x15, 0x2b, 0x90, 0x48, 0x83, 0x25, 0x34, + 0x76, 0x6a, 0x07, 0xa4, 0x71, 0x3b, 0xa1, 0x31, 0x96, 0x10, 0x74, 0x0d, 0x5a, 0x3e, 0xd9, 0x8f, + 0xd9, 0x83, 0x88, 0x06, 0x7d, 0xea, 0xd4, 0xa5, 0xc7, 0x72, 0x39, 0xfc, 0x18, 0x83, 0x8b, 0x0e, + 0xe8, 0x04, 0xd8, 0x61, 0x9c, 0xd1, 0x34, 0xf6, 0x23, 0x27, 0x58, 0xb3, 0xd6, 0xdb, 0xb8, 0x79, + 0xc6, 0x6c, 0xb8, 0x3f, 0x58, 0x50, 0x15, 0x25, 0x43, 0x37, 0x61, 0x9e, 0xb0, 0x38, 0xa6, 0x24, + 0x63, 0xa9, 0x97, 0x8d, 0x12, 0x2a, 0x2b, 0x3c, 0xdf, 0x5b, 0xed, 0xca, 0xf6, 0x6c, 0xab, 0xaf, + 0x09, 0x68, 0x77, 0xdb, 0xe0, 0x3e, 0x1b, 0x25, 0x14, 0x77, 0x48, 0x71, 0x89, 0xae, 0x42, 0x8b, + 0xb0, 0x78, 0x37, 0xec, 0x7b, 0x5f, 0x73, 0x16, 0xcb, 0xba, 0xb7, 0xb7, 0x96, 0x9f, 0x3e, 0x5e, + 0x75, 0x68, 0x4c, 0x58, 0x10, 0xc6, 0xfd, 0x0d, 0x61, 0xe8, 0x62, 0xff, 0xc1, 0x27, 0x94, 0x73, + 0xbf, 0x4f, 0x71, 0x5d, 0x39, 0xb8, 0xbf, 0x59, 0x60, 0x9b, 0x7e, 0xa0, 0x8f, 0xa0, 0x1a, 0xfb, + 0x03, 0xd5, 0x81, 0xe6, 0xd6, 0x95, 0xa7, 0x8f, 0x57, 0xdf, 0xe8, 0x87, 0xd9, 0xde, 0x70, 0xa7, + 0x4b, 0xd8, 0x60, 0x83, 0xf2, 0x6c, 0xe8, 0xa7, 0x23, 0x35, 0x3f, 0xa5, 0x89, 0x32, 0xa7, 0xc5, + 0x32, 0xc4, 0xff, 0x21, 0xb5, 0x87, 0x55, 0xb0, 0xcd, 0xcc, 0xe4, 0xa9, 0x59, 0xff, 0x45, 0x6a, + 0x73, 0x2f, 0x23, 0xb5, 0xca, 0xec, 0xa9, 0xa1, 0xf7, 0xc1, 0xde, 0x09, 0x63, 0x01, 0xe1, 0x4e, + 0x75, 0xad, 0xb2, 0xde, 0xea, 0x9d, 0x3c, 0xf0, 0xba, 0x74, 0xb7, 0x14, 0x12, 0xe7, 0x2e, 0xe8, + 0x32, 0xb4, 0x23, 0x9f, 0x67, 0x9e, 0x76, 0xd1, 0x17, 0x62, 0xb1, 0x74, 0x7e, 0xdc, 0x12, 0x30, + 0xbd, 0x81, 0x4e, 0x6a, 0xaf, 0xfb, 0x34, 0xe5, 0x21, 0x8b, 0xe5, 0xa5, 0x68, 0x2a, 0xc8, 0x3d, + 0xb5, 0xe5, 0xfe, 0x64, 0x41, 0x43, 0x7f, 0x0e, 0xdd, 0x82, 0xa5, 0x94, 0x72, 0x36, 0x4c, 0x09, + 0xf5, 0x8a, 0x79, 0x5a, 0x33, 0xe4, 0x39, 0x6f, 0x3c, 0xb7, 0x55, 0xbe, 0xef, 0x00, 0x10, 0x16, + 0x45, 0x94, 0x64, 0xa1, 0x1e, 0x82, 0x56, 0x6f, 0x49, 0x1f, 0x37, 0xdf, 0x17, 0x27, 0xde, 0xaa, + 0x3e, 0x7a, 0xbc, 0x7a, 0x04, 0x17, 0xd0, 0xc8, 0x05, 0x7b, 0xc7, 0x27, 0xfb, 0xbb, 0x61, 0x14, + 0xc9, 0x1a, 0x77, 0x70, 0xbe, 0x76, 0x7f, 0xb7, 0xa0, 0x26, 0x29, 0x02, 0x9d, 0x07, 0xc3, 0x96, + 0x9a, 0xe5, 0x9e, 0x51, 0x0d, 0x83, 0x40, 0x0e, 0x34, 0x4c, 0x11, 0xe6, 0x64, 0x11, 0xcc, 0xb2, + 0x54, 0xd9, 0xea, 0x0b, 0x55, 0xb6, 0x56, 0xaa, 0x2c, 0x7a, 0x0b, 0x80, 0x67, 0x7e, 0x46, 0x55, + 0x0d, 0xeb, 0x33, 0xd4, 0xb0, 0x26, 0xf1, 0xee, 0xdf, 0x16, 0x54, 0x05, 0xb1, 0xbd, 0xac, 0x0c, + 0x5f, 0x85, 0x5a, 0xea, 0xc7, 0x7d, 0x43, 0xd3, 0x47, 0x55, 0x10, 0x2c, 0xb6, 0x64, 0x08, 0x65, + 0x9d, 0x3a, 0x6f, 0x75, 0xe6, 0xf3, 0xa2, 0x9b, 0x80, 0x38, 0xf5, 0x23, 0x1a, 0x4c, 0x0c, 0x4d, + 0x6d, 0x86, 0x00, 0x6d, 0xe5, 0xa7, 0x46, 0xc6, 0xdd, 0x80, 0x56, 0x81, 0x9d, 0xd1, 0x1a, 0xb4, + 0xc8, 0x1e, 0x25, 0xfb, 0x09, 0x0b, 0xe3, 0x8c, 0xcb, 0x0a, 0x74, 0x70, 0x71, 0xeb, 0xd4, 0xf7, + 0xf3, 0x60, 0x63, 0xca, 0x13, 0x16, 0x73, 0x8a, 0xce, 0x4d, 0x28, 0x5e, 0x51, 0x57, 0x14, 0xa0, + 0x28, 0x79, 0xef, 0x01, 0x18, 0x1d, 0xa3, 0x81, 0x1e, 0xce, 0xe5, 0xb2, 0xc7, 0x8d, 0x1c, 0x83, + 0x0b, 0x78, 0x74, 0x15, 0x9a, 0x46, 0xce, 0x02, 0x5d, 0xd3, 0x13, 0x65, 0x67, 0x73, 0x99, 0x03, + 0x3c, 0x46, 0xa3, 0x4b, 0xd0, 0x10, 0xc2, 0x16, 0xd2, 0x40, 0xcf, 0xd9, 0xf1, 0xb2, 0xe3, 0xa6, + 0x02, 0x60, 0x83, 0x44, 0x17, 0xa1, 0x2e, 0x14, 0x8e, 0x06, 0xfa, 0xd6, 0x3b, 0x65, 0x9f, 0xdb, + 0xd2, 0x8e, 0x35, 0x0e, 0xbd, 0x09, 0xb6, 0x86, 0x04, 0x5a, 0x08, 0xdd, 0xb2, 0x8f, 0x9e, 0xa2, + 0x00, 0xe7, 0x58, 0xc1, 0x93, 0xea, 0x12, 0x07, 0x1e, 0x27, 0x7b, 0x74, 0xe0, 0x3b, 0xb6, 0xf4, + 0x5e, 0x7d, 0x46, 0x35, 0x15, 0xee, 0xae, 0x84, 0xe1, 0x0e, 0x2f, 0x2e, 0x45, 0x7d, 0xc7, 0x7d, + 0x72, 0x1a, 0x07, 0xd5, 0x77, 0x3b, 0xc7, 0xe0, 0x02, 0xfe, 0x70, 0x25, 0xfe, 0x65, 0x4e, 0x2b, + 0xb1, 0x0b, 0xb6, 0xe1, 0x7c, 0x3d, 0x1b, 0xf9, 0x5a, 0x4c, 0xa4, 0x1e, 0x45, 0x95, 0xc6, 0xec, + 0x4a, 0xd4, 0x56, 0x7e, 0x3a, 0x8f, 0x2f, 0xe0, 0xc4, 0x34, 0x21, 0x16, 0x03, 0xce, 0xc2, 0xff, + 0x4b, 0x93, 0xbc, 0xa8, 0x03, 0x9f, 0x87, 0xc5, 0x80, 0x91, 0xe1, 0x80, 0xc6, 0x99, 0x2f, 0x28, + 0xcf, 0x1b, 0xa6, 0x91, 0x9c, 0x88, 0x26, 0x5e, 0x98, 0x30, 0x7c, 0x9e, 0x46, 0xe8, 0x0c, 0xd4, + 0x99, 0x3f, 0xcc, 0xf6, 0x7a, 0xba, 0xff, 0x6d, 0x75, 0x81, 0x6f, 0x6f, 0x8a, 0x3d, 0xac, 0x6d, + 0xe8, 0x32, 0x1c, 0xcb, 0xcf, 0x9a, 0xf8, 0xd9, 0x9e, 0x27, 0x8b, 0x49, 0x53, 0xee, 0xd4, 0xd7, + 0x2a, 0xeb, 0xcd, 0xf1, 0x41, 0xee, 0xf8, 0xd9, 0xde, 0x1d, 0x6d, 0x73, 0x7f, 0xac, 0x00, 0x8c, + 0xc7, 0x1c, 0x7d, 0x50, 0x50, 0x29, 0x4b, 0xaa, 0xd4, 0x99, 0xc3, 0xae, 0x45, 0x59, 0xa8, 0xdc, + 0x9f, 0xe7, 0xc6, 0x7a, 0x72, 0x16, 0x16, 0x52, 0x4a, 0xd8, 0x60, 0x40, 0xe3, 0x80, 0x06, 0xde, + 0x58, 0xcd, 0xf1, 0xd1, 0xc2, 0xfe, 0xa7, 0x42, 0xa1, 0x0f, 0x92, 0x9e, 0xb9, 0x17, 0x90, 0x9e, + 0x5b, 0xb0, 0x64, 0x6a, 0xf8, 0xdc, 0xed, 0x9a, 0x37, 0x9e, 0xba, 0x51, 0x0b, 0x50, 0xd9, 0xa7, + 0x23, 0xa9, 0xd8, 0x4d, 0x2c, 0x7e, 0x0a, 0x9e, 0x0d, 0x42, 0xee, 0xef, 0x44, 0x4a, 0x84, 0x6d, + 0x6c, 0x96, 0xe8, 0x34, 0x74, 0x26, 0x3a, 0xa0, 0x0b, 0xdf, 0x2e, 0x16, 0x1e, 0xbd, 0x06, 0x47, + 0x43, 0xee, 0xed, 0xfa, 0x51, 0x24, 0x24, 0xcd, 0x13, 0xc1, 0x1b, 0x32, 0x4c, 0x27, 0xe4, 0x37, + 0xf5, 0xee, 0xc7, 0x74, 0xe4, 0x7e, 0x07, 0xcd, 0x9c, 0x41, 0xd0, 0xf5, 0x52, 0x5b, 0x4e, 0x1f, + 0x42, 0x38, 0xcf, 0xe8, 0x4a, 0x77, 0xdc, 0x94, 0xd2, 0x29, 0xad, 0xf2, 0x29, 0xdd, 0x00, 0x1a, + 0x9a, 0x86, 0xd0, 0xeb, 0x80, 0x7c, 0x29, 0xcb, 0x5e, 0x40, 0x39, 0x49, 0xc3, 0x44, 0x0a, 0xba, + 0x6a, 0xe3, 0xa2, 0xb2, 0xdc, 0x18, 0x1b, 0xd0, 0x39, 0x50, 0xaa, 0x30, 0x2d, 0xf9, 0xfa, 0x19, + 0x75, 0x57, 0xd8, 0x8c, 0xd0, 0x7d, 0x08, 0x75, 0x45, 0x5c, 0xe8, 0x5d, 0x38, 0x4e, 0xbf, 0x4d, + 0xa2, 0x90, 0x84, 0x99, 0x57, 0x78, 0x94, 0x8b, 0x46, 0x28, 0xe6, 0xb7, 0xb1, 0x63, 0x00, 0x9b, + 0x53, 0x76, 0xf7, 0x4b, 0xb0, 0x0d, 0x97, 0x89, 0xee, 0xe8, 0xa4, 0x35, 0x29, 0x98, 0x25, 0xba, + 0x04, 0x76, 0xc0, 0xc8, 0xec, 0x53, 0x55, 0x09, 0x18, 0x71, 0x23, 0xe8, 0x4c, 0x10, 0xdd, 0x21, + 0xf1, 0x37, 0xa1, 0xf5, 0xbc, 0x64, 0x33, 0x35, 0x6c, 0xee, 0xdb, 0x00, 0x63, 0x4a, 0x1c, 0x57, + 0xd2, 0xfa, 0xd7, 0x4a, 0xf6, 0xae, 0x43, 0x33, 0x37, 0xa0, 0x1e, 0x34, 0xcc, 0x33, 0x65, 0x61, + 0xfa, 0x8d, 0xe9, 0x2e, 0x96, 0x06, 0x67, 0xdd, 0xba, 0x68, 0x6d, 0x5d, 0x7b, 0xf4, 0xc7, 0xca, + 0x91, 0x47, 0x4f, 0x56, 0xac, 0x5f, 0x9f, 0xac, 0x58, 0x0f, 0xff, 0x5c, 0xb1, 0xbe, 0xba, 0x30, + 0xd3, 0x63, 0x5b, 0x07, 0xdb, 0xa9, 0xcb, 0xad, 0x4b, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0xc3, + 0x81, 0x15, 0xa2, 0xe5, 0x0e, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1631,6 +1638,13 @@ func (m *Request_Open) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.SealedConfigJson) > 0 { + i -= len(m.SealedConfigJson) + copy(dAtA[i:], m.SealedConfigJson) + i = encodeVarintCapture(dAtA, i, uint64(len(m.SealedConfigJson))) + i-- + dAtA[i] = 0x2a + } if len(m.StateJson) > 0 { i -= len(m.StateJson) copy(dAtA[i:], m.StateJson) @@ -2519,6 +2533,10 @@ func (m *Request_Open) ProtoSize() (n int) { if l > 0 { n += 1 + l + sovCapture(uint64(l)) } + l = len(m.SealedConfigJson) + if l > 0 { + n += 1 + l + sovCapture(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -4120,6 +4138,40 @@ func (m *Request_Open) Unmarshal(dAtA []byte) error { m.StateJson = []byte{} } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SealedConfigJson", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCapture + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCapture + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCapture + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SealedConfigJson = append(m.SealedConfigJson[:0], dAtA[iNdEx:postIndex]...) + if m.SealedConfigJson == nil { + m.SealedConfigJson = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipCapture(dAtA[iNdEx:]) diff --git a/go/protocols/capture/capture.proto b/go/protocols/capture/capture.proto index 1275dcb280c..4a0e9f99470 100644 --- a/go/protocols/capture/capture.proto +++ b/go/protocols/capture/capture.proto @@ -182,6 +182,15 @@ message Request { (gogoproto.casttype) = "encoding/json.RawMessage", json_name = "state" ]; + // Sealed (encrypted) endpoint configuration of this capture. + // This is the SOPS-encrypted document from which the runtime derived the + // decrypted `capture.config_json`, including any `sops` metadata stanza. + // Connectors may reuse it to emit `configUpdate`s that modify nonsensitive + // overlay fields without re-encrypting the configuration. + bytes sealed_config_json = 5 [ + (gogoproto.casttype) = "encoding/json.RawMessage", + json_name = "sealedConfig" + ]; } Open open = 5; diff --git a/go/protocols/materialize/materialize.pb.go b/go/protocols/materialize/materialize.pb.go index 6bbed513a80..c95bc1268eb 100644 --- a/go/protocols/materialize/materialize.pb.go +++ b/go/protocols/materialize/materialize.pb.go @@ -406,7 +406,13 @@ type Request_Open struct { // Range of documents to be processed by this session. Range *flow.RangeSpec `protobuf:"bytes,3,opt,name=range,proto3" json:"range,omitempty"` // Last-persisted connector checkpoint state from a previous session. - StateJson encoding_json.RawMessage `protobuf:"bytes,4,opt,name=state_json,json=state,proto3,casttype=encoding/json.RawMessage" json:"state_json,omitempty"` + StateJson encoding_json.RawMessage `protobuf:"bytes,4,opt,name=state_json,json=state,proto3,casttype=encoding/json.RawMessage" json:"state_json,omitempty"` + // Sealed (encrypted) endpoint configuration of this materialization. + // This is the SOPS-encrypted document from which the runtime derived the + // decrypted `materialization.config_json`, including any `sops` metadata stanza. + // Connectors may reuse it to emit `configUpdate`s that modify nonsensitive + // overlay fields without re-encrypting the configuration. + SealedConfigJson encoding_json.RawMessage `protobuf:"bytes,5,opt,name=sealed_config_json,json=sealedConfig,proto3,casttype=encoding/json.RawMessage" json:"sealed_config_json,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1506,128 +1512,129 @@ func init() { } var fileDescriptor_3e8b62b327f34bc6 = []byte{ - // 1921 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xcf, 0x6f, 0x1b, 0xc7, - 0x15, 0xf6, 0x52, 0xfc, 0xf9, 0x48, 0x59, 0xd4, 0x84, 0x8a, 0xe9, 0x8d, 0x63, 0xcb, 0x4a, 0x82, + // 1942 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x4f, 0x6f, 0x1b, 0xc7, + 0x15, 0xf7, 0x52, 0xfc, 0xfb, 0x48, 0x59, 0xd4, 0x84, 0x8a, 0xe9, 0x8d, 0x63, 0xcb, 0x4a, 0x82, 0x08, 0x2e, 0x42, 0x19, 0x72, 0xda, 0xc4, 0x0e, 0x5c, 0x94, 0xa4, 0x28, 0x80, 0xaa, 0x24, 0x2a, 0x23, 0xcb, 0x01, 0x72, 0x21, 0x46, 0xbb, 0x23, 0x6a, 0xad, 0xe5, 0xce, 0x76, 0x67, 0x69, 0x9b, - 0xbd, 0x17, 0x45, 0x7b, 0x6a, 0x8f, 0x0d, 0x7a, 0xe8, 0xa9, 0xd7, 0x9e, 0x0a, 0x14, 0xbd, 0x16, - 0x05, 0x7c, 0xec, 0x5f, 0xe0, 0xa2, 0xe9, 0x3f, 0xd0, 0x4b, 0x2f, 0x39, 0x15, 0xf3, 0x63, 0x97, - 0x4b, 0x9a, 0xa4, 0x28, 0xc0, 0xcd, 0x85, 0xd8, 0x79, 0xf3, 0x7d, 0x6f, 0xdf, 0xbc, 0x79, 0x33, - 0xef, 0xe3, 0xc2, 0xbd, 0x1e, 0xdb, 0xf2, 0x03, 0x16, 0x32, 0x8b, 0xb9, 0x7c, 0xab, 0x4f, 0x42, - 0x1a, 0x38, 0xc4, 0x75, 0x7e, 0x4e, 0x93, 0xcf, 0x35, 0x89, 0x40, 0xc5, 0x84, 0xc9, 0x5c, 0xb7, - 0x98, 0xc7, 0x07, 0x7d, 0x1a, 0xc4, 0xf4, 0xf8, 0x41, 0xc1, 0xcd, 0x5b, 0x63, 0xae, 0xcf, 0x5c, - 0xf6, 0x42, 0xfe, 0xe8, 0xd9, 0x4a, 0x8f, 0xf5, 0x98, 0x7c, 0xdc, 0x12, 0x4f, 0xca, 0xba, 0xf1, - 0xa7, 0x35, 0xc8, 0x61, 0xfa, 0xb3, 0x01, 0xe5, 0x21, 0xfa, 0x04, 0xd2, 0xdc, 0xa7, 0x56, 0xd5, - 0x58, 0x37, 0x36, 0x8b, 0xdb, 0x37, 0x6b, 0xc9, 0x80, 0x34, 0xa6, 0x76, 0xec, 0x53, 0x0b, 0x4b, - 0x18, 0x7a, 0x08, 0xf9, 0xe7, 0xc4, 0x75, 0x6c, 0x12, 0xd2, 0x6a, 0x4a, 0x52, 0xde, 0x9f, 0x4a, - 0x79, 0xaa, 0x41, 0x38, 0x86, 0xa3, 0xfb, 0x90, 0x21, 0xbe, 0xef, 0x0e, 0xab, 0x4b, 0x92, 0x67, - 0x4e, 0xe5, 0xd5, 0x05, 0x02, 0x2b, 0xa0, 0x88, 0x8d, 0xf9, 0xd4, 0xab, 0xa6, 0xe7, 0xc4, 0xd6, - 0xf1, 0xa9, 0x87, 0x25, 0x4c, 0xc0, 0x5d, 0x46, 0xec, 0x6a, 0x66, 0x0e, 0x7c, 0x9f, 0x11, 0x1b, - 0x4b, 0x98, 0x88, 0xe7, 0xcc, 0x1d, 0xf0, 0xf3, 0x6a, 0x76, 0x4e, 0x3c, 0xbb, 0x02, 0x81, 0x15, - 0x50, 0x30, 0x78, 0xc8, 0x02, 0x5a, 0xcd, 0xcd, 0x61, 0x1c, 0x0b, 0x04, 0x56, 0x40, 0xd4, 0x84, - 0x12, 0x0f, 0x49, 0x10, 0x76, 0x2d, 0xd6, 0xef, 0x3b, 0x61, 0x35, 0x2f, 0x89, 0xeb, 0x33, 0x88, - 0x24, 0x08, 0x9b, 0x12, 0x87, 0x8b, 0x7c, 0x34, 0x40, 0x0d, 0x28, 0x12, 0xeb, 0xc2, 0x63, 0x2f, - 0x5c, 0x6a, 0xf7, 0x68, 0xb5, 0x30, 0xc7, 0x47, 0x7d, 0x84, 0xc3, 0x49, 0x12, 0x7a, 0x0f, 0xf2, - 0x8e, 0x17, 0xd2, 0xc0, 0x23, 0x6e, 0xd5, 0x5e, 0x37, 0x36, 0x4b, 0xb8, 0xf0, 0x61, 0x64, 0x30, - 0x7f, 0x6b, 0x40, 0x5a, 0xec, 0x31, 0x3a, 0x84, 0xeb, 0x16, 0xf3, 0x3c, 0x6a, 0x85, 0x2c, 0xe8, - 0x86, 0x43, 0x9f, 0xca, 0xb2, 0xb8, 0xbe, 0xfd, 0x71, 0x4d, 0xd6, 0xd4, 0x41, 0xfc, 0x46, 0x12, - 0x3a, 0xcc, 0x13, 0x94, 0x5a, 0x33, 0xc2, 0x3f, 0x19, 0xfa, 0x14, 0x2f, 0x5b, 0xc9, 0x21, 0x7a, - 0x08, 0x45, 0x8b, 0x79, 0x67, 0x4e, 0xaf, 0xfb, 0x8c, 0x33, 0x4f, 0x16, 0x4c, 0xa9, 0x71, 0xeb, - 0xbb, 0xd7, 0x77, 0xaa, 0xd4, 0xb3, 0x98, 0xed, 0x78, 0xbd, 0x2d, 0x31, 0x51, 0xc3, 0xe4, 0xc5, - 0x01, 0xe5, 0x9c, 0xf4, 0x28, 0xce, 0x2a, 0x82, 0xf9, 0x97, 0x2c, 0xe4, 0xa3, 0x22, 0x42, 0x5f, - 0x42, 0xda, 0x23, 0x7d, 0x15, 0x4d, 0xa1, 0xf1, 0xf8, 0xbb, 0xd7, 0x77, 0x1e, 0xf6, 0x9c, 0xf0, - 0x7c, 0x70, 0x5a, 0xb3, 0x58, 0x7f, 0x8b, 0xf2, 0x70, 0x40, 0x82, 0xa1, 0x2a, 0xfe, 0x37, 0x8e, - 0xc3, 0x64, 0xd4, 0x58, 0xba, 0x9a, 0xb2, 0xd4, 0xd4, 0xdb, 0x5c, 0xea, 0xd2, 0xe2, 0x4b, 0x45, - 0x75, 0xc8, 0x9f, 0x3a, 0x9e, 0x80, 0xf0, 0x6a, 0x7a, 0x7d, 0x69, 0xb3, 0xb8, 0xfd, 0xd1, 0xdc, - 0x33, 0x55, 0x6b, 0x28, 0x34, 0x8e, 0x69, 0x68, 0x1f, 0x2a, 0x2e, 0xe1, 0x61, 0xb7, 0x3f, 0x1e, - 0x76, 0x7c, 0x14, 0x66, 0xad, 0x09, 0xbf, 0x23, 0x68, 0x13, 0x13, 0xe8, 0x2e, 0x94, 0xa4, 0xb7, - 0xe7, 0x34, 0xe0, 0xc2, 0x8b, 0x38, 0x20, 0x05, 0x5c, 0x14, 0xb6, 0xa7, 0xca, 0x64, 0xfe, 0x6e, - 0x09, 0x72, 0x3a, 0x0c, 0xb4, 0x07, 0x95, 0x80, 0x72, 0x36, 0x08, 0x2c, 0xda, 0x4d, 0xe6, 0xc0, - 0x58, 0x20, 0x07, 0xd7, 0x23, 0x66, 0x53, 0xe5, 0xe2, 0x11, 0x80, 0xc5, 0x5c, 0x97, 0x5a, 0x32, - 0x7c, 0x75, 0xc3, 0x54, 0x54, 0xf8, 0xcd, 0xd8, 0x2e, 0x22, 0x6f, 0xa4, 0x5f, 0xbd, 0xbe, 0x73, - 0x0d, 0x27, 0xd0, 0xe8, 0x97, 0x06, 0xac, 0x9d, 0x39, 0xd4, 0xb5, 0x93, 0x51, 0x74, 0xfb, 0xc4, - 0xaf, 0x2e, 0xc9, 0xac, 0x3e, 0x5e, 0x28, 0xab, 0xb5, 0x5d, 0xe1, 0x42, 0x85, 0xb3, 0xc7, 0x99, - 0x77, 0x40, 0xfc, 0x96, 0x17, 0x06, 0xc3, 0xc6, 0xad, 0x5f, 0xff, 0x73, 0xce, 0x42, 0x8a, 0x67, - 0x23, 0x1a, 0x32, 0x21, 0x7f, 0x4a, 0xac, 0x8b, 0x33, 0xc7, 0x75, 0xe5, 0xe5, 0xb5, 0x8c, 0xe3, - 0x31, 0xba, 0x09, 0xf9, 0x5e, 0xc0, 0x06, 0x7e, 0xf7, 0x74, 0x58, 0xcd, 0xac, 0x2f, 0x6d, 0x16, - 0x70, 0x4e, 0x8e, 0x1b, 0x43, 0xb3, 0x05, 0x37, 0x66, 0xbc, 0x1c, 0x95, 0x61, 0xe9, 0x82, 0x0e, - 0xd5, 0x01, 0xc0, 0xe2, 0x11, 0x55, 0x20, 0xf3, 0x9c, 0xb8, 0x03, 0x55, 0xb7, 0x25, 0xac, 0x06, - 0x8f, 0x52, 0x9f, 0x1b, 0xe6, 0x6f, 0x52, 0x90, 0x91, 0xf7, 0x28, 0x6a, 0xc2, 0xca, 0x64, 0x45, - 0x18, 0x97, 0x55, 0xc4, 0x24, 0x03, 0x55, 0x21, 0x17, 0x15, 0x42, 0x4a, 0xbe, 0x3e, 0x1a, 0xce, - 0xac, 0xba, 0xf4, 0x5b, 0xa9, 0xba, 0xcc, 0x1b, 0x55, 0x87, 0x3e, 0x03, 0xe0, 0x21, 0x09, 0xa9, - 0xaa, 0xaf, 0xec, 0x02, 0xf5, 0x95, 0x91, 0x78, 0xf3, 0xef, 0x06, 0xa4, 0x45, 0xa7, 0xf8, 0x7f, - 0x67, 0xe4, 0x23, 0xc8, 0x04, 0xc4, 0xeb, 0x51, 0xdd, 0xe3, 0x56, 0x94, 0x53, 0x2c, 0x4c, 0xd2, - 0x95, 0x9a, 0x9d, 0x58, 0x47, 0x7a, 0xf1, 0x75, 0x84, 0x90, 0x16, 0x1d, 0x4c, 0x44, 0xa0, 0xcf, - 0xbe, 0x0c, 0x7f, 0x19, 0x47, 0x43, 0xf4, 0x00, 0xf2, 0x17, 0x74, 0xb8, 0xf8, 0x7d, 0x2b, 0x6b, - 0xe9, 0x7d, 0x00, 0x41, 0xf2, 0x89, 0x75, 0x41, 0x6d, 0x75, 0x77, 0xe1, 0xc2, 0x05, 0x1d, 0x1e, - 0x49, 0x83, 0xd9, 0x81, 0x8c, 0xec, 0x83, 0x68, 0x17, 0x90, 0x8a, 0xdb, 0x27, 0xa1, 0x75, 0x4e, - 0xf9, 0xe2, 0xe7, 0xbc, 0x24, 0x79, 0x47, 0x8a, 0x66, 0xfe, 0x35, 0x05, 0x19, 0xd9, 0x27, 0xbf, - 0xdf, 0x85, 0x88, 0x4b, 0x5a, 0x1e, 0x13, 0xbe, 0x78, 0xe2, 0xb3, 0x8a, 0x80, 0x3e, 0x80, 0x65, - 0x4d, 0xd5, 0xce, 0x33, 0xd2, 0x79, 0x49, 0x19, 0xb5, 0xff, 0x07, 0x90, 0xb7, 0x99, 0xb5, 0x78, - 0x75, 0x2e, 0xd9, 0xcc, 0x42, 0xef, 0x42, 0x96, 0xbe, 0x74, 0x78, 0xc8, 0xa5, 0xac, 0xc8, 0x63, - 0x3d, 0x12, 0x76, 0x9b, 0xba, 0x34, 0xa4, 0x52, 0x35, 0xe4, 0xb1, 0x1e, 0x99, 0xdf, 0x18, 0x50, - 0x4c, 0x68, 0x05, 0xd4, 0x04, 0x14, 0x0c, 0xbc, 0xd0, 0xe9, 0xd3, 0xae, 0x75, 0x4e, 0xad, 0x0b, - 0x9f, 0x39, 0x5e, 0xa8, 0xab, 0xba, 0x52, 0x8b, 0x04, 0x64, 0xad, 0x19, 0xcf, 0xe1, 0x55, 0x8d, - 0x1f, 0x99, 0x66, 0xec, 0x6c, 0xea, 0xca, 0x3b, 0x7b, 0x02, 0xc5, 0x84, 0x06, 0x79, 0x5b, 0x05, - 0xb3, 0xf1, 0x0d, 0x82, 0x3c, 0xa6, 0xdc, 0x67, 0x1e, 0xa7, 0xa8, 0x36, 0x26, 0x59, 0x27, 0x55, - 0x98, 0x02, 0x25, 0x35, 0xeb, 0x63, 0x28, 0x44, 0x22, 0xd4, 0xd6, 0x2d, 0xe5, 0xce, 0x74, 0x52, - 0xd4, 0x0b, 0x6c, 0x3c, 0x62, 0xa0, 0xcf, 0x20, 0x27, 0xe4, 0xa8, 0xa3, 0x0b, 0xea, 0x4d, 0xc5, - 0xab, 0xc9, 0x75, 0x05, 0xc2, 0x11, 0x1a, 0x7d, 0x0a, 0x59, 0xa1, 0x4b, 0xa9, 0xad, 0x2f, 0xc4, - 0x5b, 0xd3, 0x79, 0x1d, 0x89, 0xc1, 0x1a, 0x2b, 0x58, 0x42, 0x9e, 0xd2, 0x48, 0xc7, 0xce, 0x60, - 0xed, 0x4b, 0x0c, 0xd6, 0x58, 0x11, 0xa4, 0xd4, 0xa8, 0xd4, 0xd6, 0x72, 0x76, 0x46, 0x90, 0xbb, - 0x0a, 0x84, 0x23, 0x34, 0xda, 0x83, 0xeb, 0x52, 0x6b, 0x52, 0x3b, 0xd2, 0xa8, 0x4a, 0xdc, 0x7e, - 0x30, 0x23, 0xad, 0x0a, 0xab, 0x65, 0xea, 0x32, 0x4f, 0x0e, 0xd1, 0x2e, 0x94, 0x12, 0x9a, 0xd3, - 0xd6, 0x6a, 0x77, 0x63, 0x46, 0xba, 0x12, 0x48, 0x3c, 0xc6, 0x9b, 0x2f, 0x56, 0x7f, 0x9f, 0xd2, - 0x62, 0xd5, 0x84, 0x7c, 0xa4, 0xf4, 0xf4, 0xdd, 0x11, 0x8f, 0x45, 0xdd, 0x69, 0x0d, 0xc0, 0xad, - 0x73, 0xda, 0x27, 0x57, 0x28, 0x67, 0xc5, 0x3b, 0x96, 0x34, 0xf4, 0x15, 0xbc, 0x37, 0x29, 0x6d, - 0x92, 0x0e, 0x17, 0x51, 0x79, 0x95, 0x71, 0x85, 0xa3, 0x1d, 0xff, 0x00, 0x56, 0x6d, 0x66, 0x0d, - 0xfa, 0xd4, 0x0b, 0x65, 0x4f, 0xe9, 0x0e, 0x02, 0x25, 0x15, 0x0a, 0xb8, 0x3c, 0x36, 0x71, 0x12, - 0xb8, 0xe8, 0x43, 0xc8, 0x32, 0x32, 0x08, 0xcf, 0xb7, 0x75, 0x49, 0x94, 0x54, 0x5b, 0xe9, 0xd4, - 0x85, 0x0d, 0xeb, 0xb9, 0xbd, 0x74, 0x3e, 0x5b, 0xce, 0x99, 0xff, 0xcd, 0x41, 0x21, 0x2e, 0x63, - 0xd4, 0x4c, 0x48, 0x4b, 0x43, 0x8a, 0xa0, 0x8f, 0x2f, 0xa9, 0xfc, 0x37, 0xc5, 0xa5, 0xf9, 0x12, - 0x2a, 0x47, 0x01, 0x7b, 0xa6, 0x54, 0x56, 0x93, 0x79, 0x3c, 0x0c, 0x88, 0xb8, 0x33, 0x2a, 0x90, - 0x91, 0xa2, 0x47, 0xab, 0x12, 0x35, 0x40, 0x7b, 0x42, 0xc1, 0x45, 0x18, 0x7d, 0xdc, 0xee, 0x5d, - 0xf6, 0xd2, 0x91, 0x57, 0x9c, 0x60, 0x9b, 0x7f, 0x4e, 0x01, 0x24, 0x5e, 0xd8, 0x84, 0x74, 0x42, - 0xa9, 0x6f, 0x2d, 0xee, 0xb4, 0x26, 0x15, 0xbb, 0x24, 0x8b, 0x6b, 0x35, 0xa0, 0x24, 0xda, 0xbd, - 0x02, 0xd6, 0x23, 0x21, 0x3f, 0xce, 0x98, 0x6b, 0x53, 0xbb, 0xab, 0x16, 0xa5, 0x36, 0xa3, 0xa8, - 0x6c, 0x52, 0x96, 0x6d, 0xfc, 0xd1, 0x80, 0xb4, 0x14, 0xfb, 0x45, 0xc8, 0xb5, 0x0f, 0x9f, 0xd6, - 0xf7, 0xdb, 0x3b, 0xe5, 0x6b, 0x08, 0xc1, 0xf5, 0xdd, 0x76, 0x6b, 0x7f, 0xa7, 0x8b, 0x5b, 0x5f, - 0x9e, 0xb4, 0x71, 0x6b, 0xa7, 0x6c, 0xa0, 0x35, 0x58, 0xdd, 0xef, 0x34, 0xeb, 0x4f, 0xda, 0x9d, - 0xc3, 0x91, 0x39, 0x85, 0xaa, 0x50, 0x49, 0x98, 0x9b, 0x9d, 0x83, 0x83, 0xd6, 0xe1, 0x4e, 0x6b, - 0xa7, 0xbc, 0x34, 0x72, 0xd2, 0x39, 0x12, 0xb3, 0xf5, 0xfd, 0x72, 0x1a, 0xbd, 0x03, 0x2b, 0xca, - 0xb6, 0xdb, 0xc1, 0x8d, 0xf6, 0xce, 0x4e, 0xeb, 0xb0, 0x9c, 0x41, 0x65, 0x28, 0xb5, 0x0f, 0x9b, - 0x9d, 0x83, 0xa3, 0xfa, 0x93, 0x76, 0x63, 0xbf, 0x55, 0xce, 0xa2, 0x55, 0x58, 0x3e, 0x39, 0x3c, - 0xae, 0x3f, 0x69, 0x1f, 0xef, 0xb6, 0xeb, 0xc2, 0x94, 0x33, 0xff, 0x93, 0x50, 0xe7, 0x3f, 0x82, - 0x1b, 0x16, 0xe1, 0xb4, 0xeb, 0x78, 0x9c, 0x7a, 0xdc, 0x09, 0x9d, 0xe7, 0x54, 0xad, 0x90, 0xcb, - 0x6a, 0xca, 0xe3, 0x35, 0x31, 0xdd, 0x1e, 0xcd, 0xca, 0xb5, 0x72, 0xf4, 0xb5, 0xfc, 0x43, 0xa3, - 0x13, 0x18, 0x55, 0xcf, 0xe7, 0x0b, 0x56, 0x4f, 0x22, 0xf7, 0x5c, 0x0a, 0x58, 0x9c, 0x74, 0x26, - 0x9a, 0x69, 0x7c, 0xac, 0x7c, 0x12, 0x9e, 0x57, 0x53, 0x52, 0x08, 0x97, 0x22, 0xe3, 0x11, 0x09, - 0xcf, 0x05, 0xc8, 0xa6, 0x6e, 0x48, 0xba, 0x03, 0x5f, 0xf8, 0xe6, 0x72, 0xbf, 0xf2, 0xb8, 0x24, - 0x8d, 0x27, 0xca, 0x86, 0x6a, 0x00, 0x9c, 0x06, 0x5d, 0x9f, 0xb9, 0x8e, 0x35, 0xd4, 0xf7, 0xac, - 0x56, 0x5d, 0xc7, 0x34, 0x38, 0x92, 0x66, 0x5c, 0xe0, 0xd1, 0x23, 0xba, 0x80, 0x77, 0xfd, 0xb8, - 0x96, 0xbb, 0xc9, 0x05, 0x66, 0xe5, 0x02, 0x3f, 0xbd, 0x6c, 0x81, 0xd3, 0x4e, 0x02, 0x5e, 0xf3, - 0xa7, 0x58, 0xb9, 0xf9, 0x0c, 0xca, 0x93, 0x79, 0x98, 0x22, 0xe4, 0x7f, 0x92, 0x14, 0xf2, 0x57, - 0x3b, 0x2b, 0x09, 0xd1, 0x6f, 0x43, 0x4e, 0x37, 0x20, 0xf4, 0x09, 0x20, 0xa2, 0xd6, 0x67, 0x53, - 0x6e, 0x05, 0x8e, 0x1f, 0xcb, 0xdc, 0x02, 0x5e, 0x55, 0x33, 0x3b, 0xa3, 0x09, 0x74, 0x0f, 0x94, - 0xb8, 0x9c, 0xfc, 0xb7, 0xa5, 0xff, 0xdd, 0x1e, 0x8b, 0xb9, 0x48, 0x7f, 0xfe, 0xca, 0x80, 0xac, - 0xea, 0x57, 0x6f, 0x47, 0x76, 0x3c, 0x82, 0x9b, 0xb6, 0xc3, 0xc9, 0xa9, 0x4b, 0xbb, 0xa2, 0x91, - 0x75, 0x99, 0x1f, 0x3a, 0xfd, 0x48, 0x98, 0xa7, 0xe4, 0x7e, 0xdf, 0xd0, 0x00, 0xd1, 0xf0, 0x3a, - 0x89, 0x69, 0xf3, 0x2b, 0xc8, 0xaa, 0x26, 0x38, 0x5f, 0x44, 0xc6, 0x82, 0x2c, 0xb5, 0xa0, 0x20, - 0x33, 0x7f, 0x08, 0x39, 0xdd, 0x26, 0x47, 0xb9, 0x31, 0x2e, 0xcf, 0xcd, 0x17, 0xb0, 0x3c, 0xd6, - 0x1d, 0xaf, 0x44, 0x7e, 0x04, 0xa5, 0x64, 0x43, 0xbc, 0x0a, 0x77, 0xe3, 0x17, 0x69, 0xc8, 0xb4, - 0x5e, 0x86, 0x01, 0x31, 0xff, 0x66, 0xc0, 0xdd, 0xa8, 0x50, 0x5a, 0x42, 0x45, 0x3a, 0x5e, 0x6f, - 0x54, 0xb0, 0xd1, 0x27, 0xbf, 0x7d, 0x28, 0x53, 0x3d, 0xd9, 0x4d, 0xe6, 0xad, 0xb8, 0x7d, 0x77, - 0xf6, 0xc7, 0x8f, 0xa8, 0x2d, 0xac, 0x44, 0xd4, 0xe8, 0x7e, 0x39, 0x82, 0xb2, 0x1f, 0x30, 0x9f, - 0x71, 0x6a, 0xc7, 0xde, 0x54, 0x25, 0x2d, 0xf8, 0x15, 0x63, 0x25, 0xa2, 0x6b, 0x83, 0xb8, 0xf5, - 0xe3, 0x55, 0x68, 0x5b, 0xbd, 0x47, 0x1c, 0x8f, 0x87, 0x89, 0xd3, 0x84, 0xbe, 0x18, 0xdf, 0xf4, - 0x85, 0x82, 0x8f, 0xeb, 0xa2, 0x37, 0x7e, 0xb9, 0xa5, 0xe4, 0xd9, 0x6f, 0x8d, 0xc5, 0x2b, 0x33, - 0x5a, 0xbb, 0x34, 0x8e, 0xf9, 0x37, 0xdd, 0xf7, 0x79, 0x05, 0x6c, 0xff, 0x14, 0x0a, 0x71, 0x81, - 0xa0, 0x1f, 0x43, 0x71, 0x94, 0x09, 0x8a, 0x2a, 0xd3, 0xf6, 0xc2, 0x5c, 0x9b, 0xfa, 0xa2, 0x4d, - 0xe3, 0xbe, 0xd1, 0x68, 0xbc, 0xfa, 0xd7, 0xed, 0x6b, 0xaf, 0xbe, 0xbd, 0x6d, 0xfc, 0xe3, 0xdb, - 0xdb, 0xc6, 0x1f, 0xfe, 0x7d, 0xdb, 0xf8, 0xfa, 0xfe, 0x42, 0x9f, 0xdc, 0x12, 0x0e, 0x4f, 0xb3, - 0xd2, 0xfc, 0xe0, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x3c, 0xcd, 0x0e, 0x34, 0xff, 0x16, 0x00, - 0x00, + 0xbd, 0x17, 0x45, 0x0b, 0x14, 0x68, 0x8f, 0x0d, 0x7a, 0xe8, 0xa9, 0xdf, 0xa0, 0x40, 0xd1, 0x6b, + 0x2f, 0x3e, 0xf6, 0x13, 0xb8, 0x68, 0xfa, 0x05, 0x7a, 0x29, 0x0a, 0xe4, 0x54, 0xcc, 0x9f, 0x5d, + 0x2e, 0x69, 0x92, 0xa2, 0x00, 0x37, 0x17, 0x62, 0xe7, 0xcd, 0xef, 0xf7, 0x76, 0xde, 0xcc, 0x9b, + 0xf7, 0x7e, 0x4b, 0xb8, 0xd7, 0x63, 0x5b, 0x7e, 0xc0, 0x42, 0x66, 0x31, 0x97, 0x6f, 0xf5, 0x49, + 0x48, 0x03, 0x87, 0xb8, 0xce, 0xcf, 0x69, 0xf2, 0xb9, 0x26, 0x11, 0xa8, 0x98, 0x30, 0x99, 0xeb, + 0x16, 0xf3, 0xf8, 0xa0, 0x4f, 0x83, 0x98, 0x1e, 0x3f, 0x28, 0xb8, 0x79, 0x6b, 0xcc, 0xf5, 0x99, + 0xcb, 0x5e, 0xc8, 0x1f, 0x3d, 0x5b, 0xe9, 0xb1, 0x1e, 0x93, 0x8f, 0x5b, 0xe2, 0x49, 0x59, 0x37, + 0xfe, 0xbb, 0x06, 0x39, 0x4c, 0x7f, 0x36, 0xa0, 0x3c, 0x44, 0x9f, 0x40, 0x9a, 0xfb, 0xd4, 0xaa, + 0x1a, 0xeb, 0xc6, 0x66, 0x71, 0xfb, 0x66, 0x2d, 0xb9, 0x20, 0x8d, 0xa9, 0x1d, 0xfb, 0xd4, 0xc2, + 0x12, 0x86, 0x1e, 0x42, 0xfe, 0x39, 0x71, 0x1d, 0x9b, 0x84, 0xb4, 0x9a, 0x92, 0x94, 0xf7, 0xa7, + 0x52, 0x9e, 0x6a, 0x10, 0x8e, 0xe1, 0xe8, 0x3e, 0x64, 0x88, 0xef, 0xbb, 0xc3, 0xea, 0x92, 0xe4, + 0x99, 0x53, 0x79, 0x75, 0x81, 0xc0, 0x0a, 0x28, 0xd6, 0xc6, 0x7c, 0xea, 0x55, 0xd3, 0x73, 0xd6, + 0xd6, 0xf1, 0xa9, 0x87, 0x25, 0x4c, 0xc0, 0x5d, 0x46, 0xec, 0x6a, 0x66, 0x0e, 0x7c, 0x9f, 0x11, + 0x1b, 0x4b, 0x98, 0x58, 0xcf, 0x99, 0x3b, 0xe0, 0xe7, 0xd5, 0xec, 0x9c, 0xf5, 0xec, 0x0a, 0x04, + 0x56, 0x40, 0xc1, 0xe0, 0x21, 0x0b, 0x68, 0x35, 0x37, 0x87, 0x71, 0x2c, 0x10, 0x58, 0x01, 0x51, + 0x13, 0x4a, 0x3c, 0x24, 0x41, 0xd8, 0xb5, 0x58, 0xbf, 0xef, 0x84, 0xd5, 0xbc, 0x24, 0xae, 0xcf, + 0x20, 0x92, 0x20, 0x6c, 0x4a, 0x1c, 0x2e, 0xf2, 0xd1, 0x00, 0x35, 0xa0, 0x48, 0xac, 0x0b, 0x8f, + 0xbd, 0x70, 0xa9, 0xdd, 0xa3, 0xd5, 0xc2, 0x1c, 0x1f, 0xf5, 0x11, 0x0e, 0x27, 0x49, 0xe8, 0x3d, + 0xc8, 0x3b, 0x5e, 0x48, 0x03, 0x8f, 0xb8, 0x55, 0x7b, 0xdd, 0xd8, 0x2c, 0xe1, 0xc2, 0x87, 0x91, + 0xc1, 0xfc, 0x9d, 0x01, 0x69, 0x71, 0xc6, 0xe8, 0x10, 0xae, 0x5b, 0xcc, 0xf3, 0xa8, 0x15, 0xb2, + 0xa0, 0x1b, 0x0e, 0x7d, 0x2a, 0xd3, 0xe2, 0xfa, 0xf6, 0xc7, 0x35, 0x99, 0x53, 0x07, 0xf1, 0x1b, + 0x49, 0xe8, 0x30, 0x4f, 0x50, 0x6a, 0xcd, 0x08, 0xff, 0x64, 0xe8, 0x53, 0xbc, 0x6c, 0x25, 0x87, + 0xe8, 0x21, 0x14, 0x2d, 0xe6, 0x9d, 0x39, 0xbd, 0xee, 0x33, 0xce, 0x3c, 0x99, 0x30, 0xa5, 0xc6, + 0xad, 0xef, 0x5e, 0xdf, 0xa9, 0x52, 0xcf, 0x62, 0xb6, 0xe3, 0xf5, 0xb6, 0xc4, 0x44, 0x0d, 0x93, + 0x17, 0x07, 0x94, 0x73, 0xd2, 0xa3, 0x38, 0xab, 0x08, 0xe6, 0x5f, 0xb2, 0x90, 0x8f, 0x92, 0x08, + 0x7d, 0x09, 0x69, 0x8f, 0xf4, 0xd5, 0x6a, 0x0a, 0x8d, 0xc7, 0xdf, 0xbd, 0xbe, 0xf3, 0xb0, 0xe7, + 0x84, 0xe7, 0x83, 0xd3, 0x9a, 0xc5, 0xfa, 0x5b, 0x94, 0x87, 0x03, 0x12, 0x0c, 0x55, 0xf2, 0xbf, + 0x71, 0x1d, 0x26, 0x57, 0x8d, 0xa5, 0xab, 0x29, 0xa1, 0xa6, 0xde, 0x66, 0xa8, 0x4b, 0x8b, 0x87, + 0x8a, 0xea, 0x90, 0x3f, 0x75, 0x3c, 0x01, 0xe1, 0xd5, 0xf4, 0xfa, 0xd2, 0x66, 0x71, 0xfb, 0xa3, + 0xb9, 0x77, 0xaa, 0xd6, 0x50, 0x68, 0x1c, 0xd3, 0xd0, 0x3e, 0x54, 0x5c, 0xc2, 0xc3, 0x6e, 0x7f, + 0x7c, 0xd9, 0xf1, 0x55, 0x98, 0x15, 0x13, 0x7e, 0x47, 0xd0, 0x26, 0x26, 0xd0, 0x5d, 0x28, 0x49, + 0x6f, 0xcf, 0x69, 0xc0, 0x85, 0x17, 0x71, 0x41, 0x0a, 0xb8, 0x28, 0x6c, 0x4f, 0x95, 0xc9, 0xfc, + 0xfd, 0x12, 0xe4, 0xf4, 0x32, 0xd0, 0x1e, 0x54, 0x02, 0xca, 0xd9, 0x20, 0xb0, 0x68, 0x37, 0xb9, + 0x07, 0xc6, 0x02, 0x7b, 0x70, 0x3d, 0x62, 0x36, 0xd5, 0x5e, 0x3c, 0x02, 0xb0, 0x98, 0xeb, 0x52, + 0x4b, 0x2e, 0x5f, 0x55, 0x98, 0x8a, 0x5a, 0x7e, 0x33, 0xb6, 0x8b, 0x95, 0x37, 0xd2, 0xaf, 0x5e, + 0xdf, 0xb9, 0x86, 0x13, 0x68, 0xf4, 0x4b, 0x03, 0xd6, 0xce, 0x1c, 0xea, 0xda, 0xc9, 0x55, 0x74, + 0xfb, 0xc4, 0xaf, 0x2e, 0xc9, 0x5d, 0x7d, 0xbc, 0xd0, 0xae, 0xd6, 0x76, 0x85, 0x0b, 0xb5, 0x9c, + 0x3d, 0xce, 0xbc, 0x03, 0xe2, 0xb7, 0xbc, 0x30, 0x18, 0x36, 0x6e, 0xfd, 0xfa, 0x1f, 0x73, 0x02, + 0x29, 0x9e, 0x8d, 0x68, 0xc8, 0x84, 0xfc, 0x29, 0xb1, 0x2e, 0xce, 0x1c, 0xd7, 0x95, 0xc5, 0x6b, + 0x19, 0xc7, 0x63, 0x74, 0x13, 0xf2, 0xbd, 0x80, 0x0d, 0xfc, 0xee, 0xe9, 0xb0, 0x9a, 0x59, 0x5f, + 0xda, 0x2c, 0xe0, 0x9c, 0x1c, 0x37, 0x86, 0x66, 0x0b, 0x6e, 0xcc, 0x78, 0x39, 0x2a, 0xc3, 0xd2, + 0x05, 0x1d, 0xaa, 0x0b, 0x80, 0xc5, 0x23, 0xaa, 0x40, 0xe6, 0x39, 0x71, 0x07, 0x2a, 0x6f, 0x4b, + 0x58, 0x0d, 0x1e, 0xa5, 0x3e, 0x37, 0xcc, 0xdf, 0xa6, 0x20, 0x23, 0xeb, 0x28, 0x6a, 0xc2, 0xca, + 0x64, 0x46, 0x18, 0x97, 0x65, 0xc4, 0x24, 0x03, 0x55, 0x21, 0x17, 0x25, 0x42, 0x4a, 0xbe, 0x3e, + 0x1a, 0xce, 0xcc, 0xba, 0xf4, 0x5b, 0xc9, 0xba, 0xcc, 0x1b, 0x59, 0x87, 0x3e, 0x03, 0xe0, 0x21, + 0x09, 0xa9, 0xca, 0xaf, 0xec, 0x02, 0xf9, 0x95, 0x91, 0x78, 0xf3, 0x37, 0x29, 0x48, 0x8b, 0x4e, + 0xf1, 0xff, 0xde, 0x91, 0x8f, 0x20, 0x13, 0x10, 0xaf, 0x47, 0x75, 0x8f, 0x5b, 0x51, 0x4e, 0xb1, + 0x30, 0x49, 0x57, 0x6a, 0x76, 0x22, 0x8e, 0xf4, 0xc2, 0x71, 0xa0, 0x5d, 0x40, 0x9c, 0x12, 0x97, + 0x8e, 0xa5, 0xb8, 0xdc, 0xa9, 0xcb, 0x1c, 0x94, 0x14, 0x4f, 0xa5, 0x96, 0x19, 0x42, 0x5a, 0x74, + 0x42, 0x11, 0x89, 0xae, 0x21, 0x72, 0x1b, 0x96, 0x71, 0x34, 0x44, 0x0f, 0x20, 0x7f, 0x41, 0x87, + 0x8b, 0xd7, 0x6d, 0x99, 0x93, 0xef, 0x03, 0x08, 0x92, 0x4f, 0xac, 0x0b, 0x6a, 0xab, 0x1a, 0x88, + 0x0b, 0x17, 0x74, 0x78, 0x24, 0x0d, 0x66, 0x07, 0x32, 0xb2, 0x9f, 0xca, 0x30, 0x64, 0xfc, 0x3e, + 0x09, 0xad, 0x73, 0xca, 0x17, 0xaf, 0x17, 0x25, 0xc9, 0x3b, 0x52, 0x34, 0xf3, 0xaf, 0x29, 0xc8, + 0xc8, 0x7e, 0xfb, 0xfd, 0x06, 0x22, 0x8a, 0xbd, 0xbc, 0x6e, 0x7c, 0xf1, 0x03, 0xcc, 0x2a, 0x02, + 0xfa, 0x00, 0x96, 0x35, 0x55, 0x3b, 0x97, 0x87, 0x87, 0x4b, 0xca, 0xa8, 0xfd, 0x3f, 0x80, 0xbc, + 0xcd, 0xac, 0xc5, 0xb3, 0x7c, 0xc9, 0x66, 0x16, 0x7a, 0x17, 0xb2, 0xf4, 0xa5, 0xc3, 0x43, 0x2e, + 0xe5, 0x49, 0x1e, 0xeb, 0x91, 0xb0, 0xdb, 0xd4, 0xa5, 0x21, 0x95, 0xea, 0x23, 0x8f, 0xf5, 0xc8, + 0xfc, 0xc6, 0x80, 0x62, 0x42, 0x73, 0xa0, 0x26, 0xa0, 0x60, 0xe0, 0x85, 0x4e, 0x9f, 0x76, 0xad, + 0x73, 0x6a, 0x5d, 0xf8, 0xcc, 0xf1, 0x42, 0x7d, 0x3b, 0x2a, 0xb5, 0x48, 0x88, 0xd6, 0x9a, 0xf1, + 0x1c, 0x5e, 0xd5, 0xf8, 0x91, 0x69, 0xc6, 0xc9, 0xa6, 0xae, 0x7c, 0xb2, 0x27, 0x50, 0x4c, 0x68, + 0x99, 0xb7, 0x95, 0x30, 0x1b, 0xdf, 0x20, 0xc8, 0x63, 0xca, 0x7d, 0xe6, 0x71, 0x8a, 0x6a, 0x63, + 0xd2, 0x77, 0x52, 0xcd, 0x29, 0x50, 0x52, 0xfb, 0x3e, 0x86, 0x42, 0x24, 0x66, 0x6d, 0xdd, 0x9a, + 0xee, 0x4c, 0x27, 0x45, 0x3d, 0xc5, 0xc6, 0x23, 0x06, 0xfa, 0x0c, 0x72, 0x42, 0xd6, 0x3a, 0x3a, + 0xa1, 0xde, 0x54, 0xce, 0x9a, 0x5c, 0x57, 0x20, 0x1c, 0xa1, 0xd1, 0xa7, 0x90, 0x15, 0xfa, 0x96, + 0xda, 0xba, 0xb0, 0xde, 0x9a, 0xce, 0xeb, 0x48, 0x0c, 0xd6, 0x58, 0xc1, 0x12, 0x32, 0x97, 0x46, + 0x7a, 0x78, 0x06, 0x6b, 0x5f, 0x62, 0xb0, 0xc6, 0x8a, 0x45, 0x4a, 0xad, 0x4b, 0x6d, 0x2d, 0x8b, + 0x67, 0x2c, 0x72, 0x57, 0x81, 0x70, 0x84, 0x46, 0x7b, 0x70, 0x5d, 0x6a, 0x56, 0x59, 0x9a, 0xa4, + 0xd6, 0x55, 0x22, 0xf9, 0x83, 0x19, 0xdb, 0xaa, 0xb0, 0x5a, 0xee, 0x2e, 0xf3, 0xe4, 0x10, 0xed, + 0x42, 0x29, 0xa1, 0x5d, 0x6d, 0xad, 0x9a, 0x37, 0x66, 0x6c, 0x57, 0x02, 0x89, 0xc7, 0x78, 0xf3, + 0x45, 0xef, 0x1f, 0x52, 0x5a, 0xf4, 0x9a, 0x90, 0x8f, 0x14, 0xa3, 0xae, 0x1d, 0xf1, 0x58, 0xe4, + 0x9d, 0x2e, 0xb4, 0xdc, 0x3a, 0xa7, 0x7d, 0x72, 0x85, 0x74, 0x56, 0xbc, 0x63, 0x49, 0x43, 0x5f, + 0xc1, 0x7b, 0x93, 0x12, 0x29, 0xe9, 0x70, 0x11, 0xb5, 0x58, 0x19, 0x57, 0x4a, 0xda, 0xf1, 0x0f, + 0x60, 0xd5, 0x66, 0xd6, 0xa0, 0x4f, 0xbd, 0x50, 0xf6, 0xa6, 0xee, 0x20, 0x50, 0x92, 0xa3, 0x80, + 0xcb, 0x63, 0x13, 0x27, 0x81, 0x8b, 0x3e, 0x84, 0x2c, 0x23, 0x83, 0xf0, 0x7c, 0x5b, 0xa7, 0x44, + 0x49, 0xb5, 0xa7, 0x4e, 0x5d, 0xd8, 0xb0, 0x9e, 0xdb, 0x4b, 0xe7, 0xb3, 0xe5, 0x9c, 0xf9, 0x9f, + 0x1c, 0x14, 0xe2, 0x34, 0x46, 0xcd, 0x84, 0x44, 0x35, 0xa4, 0x98, 0xfa, 0xf8, 0x92, 0xcc, 0x7f, + 0x53, 0xa4, 0x9a, 0x2f, 0xa1, 0x72, 0x14, 0xb0, 0x67, 0x4a, 0xad, 0x35, 0x99, 0xc7, 0xc3, 0x80, + 0x88, 0x9a, 0x51, 0x81, 0x8c, 0x14, 0x4f, 0x5a, 0xdd, 0xa8, 0x01, 0xda, 0x13, 0x4a, 0x30, 0xc2, + 0xe8, 0xeb, 0x76, 0xef, 0xb2, 0x97, 0x8e, 0xbc, 0xe2, 0x04, 0xdb, 0xfc, 0x73, 0x0a, 0x20, 0xf1, + 0xc2, 0x26, 0xa4, 0x13, 0x8a, 0x7f, 0x6b, 0x71, 0xa7, 0x35, 0xa9, 0xfc, 0x25, 0x59, 0x94, 0xd5, + 0x80, 0x92, 0xe8, 0xf4, 0x0a, 0x58, 0x8f, 0x84, 0x8c, 0x39, 0x63, 0xae, 0x4d, 0xed, 0xae, 0x0a, + 0x4a, 0x1d, 0x46, 0x51, 0xd9, 0xa4, 0xbc, 0xdb, 0xf8, 0x93, 0x01, 0x69, 0xf9, 0xd1, 0x50, 0x84, + 0x5c, 0xfb, 0xf0, 0x69, 0x7d, 0xbf, 0xbd, 0x53, 0xbe, 0x86, 0x10, 0x5c, 0xdf, 0x6d, 0xb7, 0xf6, + 0x77, 0xba, 0xb8, 0xf5, 0xe5, 0x49, 0x1b, 0xb7, 0x76, 0xca, 0x06, 0x5a, 0x83, 0xd5, 0xfd, 0x4e, + 0xb3, 0xfe, 0xa4, 0xdd, 0x39, 0x1c, 0x99, 0x53, 0xa8, 0x0a, 0x95, 0x84, 0xb9, 0xd9, 0x39, 0x38, + 0x68, 0x1d, 0xee, 0xb4, 0x76, 0xca, 0x4b, 0x23, 0x27, 0x9d, 0x23, 0x31, 0x5b, 0xdf, 0x2f, 0xa7, + 0xd1, 0x3b, 0xb0, 0xa2, 0x6c, 0xbb, 0x1d, 0xdc, 0x68, 0xef, 0xec, 0xb4, 0x0e, 0xcb, 0x19, 0x54, + 0x86, 0x52, 0xfb, 0xb0, 0xd9, 0x39, 0x38, 0xaa, 0x3f, 0x69, 0x37, 0xf6, 0x5b, 0xe5, 0x2c, 0x5a, + 0x85, 0xe5, 0x93, 0xc3, 0xe3, 0xfa, 0x93, 0xf6, 0xf1, 0x6e, 0xbb, 0x2e, 0x4c, 0x39, 0xf3, 0xdf, + 0x09, 0x95, 0xff, 0x23, 0xb8, 0x61, 0x11, 0x4e, 0xbb, 0x8e, 0xc7, 0xa9, 0xc7, 0x9d, 0xd0, 0x79, + 0x4e, 0x55, 0x84, 0x5c, 0x66, 0x53, 0x1e, 0xaf, 0x89, 0xe9, 0xf6, 0x68, 0x56, 0xc6, 0xca, 0xd1, + 0xd7, 0xf2, 0xc3, 0x48, 0x6f, 0x60, 0x94, 0x3d, 0x9f, 0x2f, 0x98, 0x3d, 0x89, 0xbd, 0xe7, 0x52, + 0x08, 0xe3, 0xa4, 0x33, 0xd1, 0x4c, 0xe3, 0x6b, 0xe5, 0x93, 0xf0, 0xbc, 0x9a, 0x92, 0x82, 0xba, + 0x14, 0x19, 0x8f, 0x48, 0x78, 0x2e, 0x40, 0x36, 0x75, 0x43, 0xd2, 0x1d, 0xf8, 0xc2, 0x37, 0x97, + 0xe7, 0x95, 0xc7, 0x25, 0x69, 0x3c, 0x51, 0x36, 0x54, 0x03, 0xe0, 0x34, 0xe8, 0xfa, 0xcc, 0x75, + 0xac, 0xa1, 0xae, 0xb3, 0x5a, 0xbd, 0x1d, 0xd3, 0xe0, 0x48, 0x9a, 0x71, 0x81, 0x47, 0x8f, 0xe8, + 0x02, 0xde, 0xf5, 0xe3, 0x5c, 0xee, 0x26, 0x03, 0xcc, 0xca, 0x00, 0x3f, 0xbd, 0x2c, 0xc0, 0x69, + 0x37, 0x01, 0xaf, 0xf9, 0x53, 0xac, 0xdc, 0x7c, 0x06, 0xe5, 0xc9, 0x7d, 0x98, 0xf2, 0x41, 0xf0, + 0x93, 0xe4, 0x07, 0xc1, 0xd5, 0xee, 0x4a, 0xe2, 0xe3, 0xc1, 0x86, 0x9c, 0x6e, 0x40, 0xe8, 0x13, + 0x40, 0x44, 0xc5, 0x67, 0x53, 0x6e, 0x05, 0x8e, 0x1f, 0xcb, 0xe5, 0x02, 0x5e, 0x55, 0x33, 0x3b, + 0xa3, 0x09, 0x74, 0x0f, 0x94, 0x48, 0x9d, 0xfc, 0x6a, 0xd3, 0x5f, 0xc9, 0xc7, 0x62, 0x2e, 0xd2, + 0xe3, 0xbf, 0x32, 0x20, 0xab, 0xfa, 0xd5, 0xdb, 0x91, 0x1d, 0x8f, 0xe0, 0xa6, 0xed, 0x70, 0x72, + 0xea, 0xd2, 0xae, 0x68, 0x64, 0x5d, 0xe6, 0x87, 0x4e, 0x3f, 0x12, 0xf8, 0x29, 0x79, 0xde, 0x37, + 0x34, 0x40, 0x34, 0xbc, 0x4e, 0x62, 0xda, 0xfc, 0x0a, 0xb2, 0xaa, 0x09, 0xce, 0x17, 0x91, 0xb1, + 0x20, 0x4b, 0x2d, 0x28, 0xc8, 0xcc, 0x1f, 0x42, 0x4e, 0xb7, 0xc9, 0xd1, 0xde, 0x18, 0x97, 0xef, + 0xcd, 0x17, 0xb0, 0x3c, 0xd6, 0x1d, 0xaf, 0x44, 0x7e, 0x04, 0xa5, 0x64, 0x43, 0xbc, 0x0a, 0x77, + 0xe3, 0x17, 0x69, 0xc8, 0xb4, 0x5e, 0x86, 0x01, 0x31, 0xff, 0x66, 0xc0, 0xdd, 0x28, 0x51, 0x5a, + 0x42, 0x45, 0x3a, 0x5e, 0x6f, 0x94, 0xb0, 0xd1, 0x5f, 0x87, 0xfb, 0x50, 0xa6, 0x7a, 0xb2, 0x9b, + 0xdc, 0xb7, 0xe2, 0xf6, 0xdd, 0xd9, 0x7f, 0xa2, 0x44, 0x6d, 0x61, 0x25, 0xa2, 0x46, 0xf5, 0xe5, + 0x08, 0xca, 0x7e, 0xc0, 0x7c, 0xc6, 0xa9, 0x1d, 0x7b, 0x53, 0x99, 0xb4, 0xe0, 0xbf, 0x21, 0x2b, + 0x11, 0x5d, 0x1b, 0x44, 0xd5, 0x8f, 0xa3, 0xd0, 0xb6, 0x7a, 0x8f, 0x38, 0x1e, 0x0f, 0x13, 0xb7, + 0x09, 0x7d, 0x31, 0x7e, 0xe8, 0x0b, 0x2d, 0x3e, 0xce, 0x8b, 0xde, 0x78, 0x71, 0x4b, 0xc9, 0xbb, + 0xdf, 0x1a, 0x5b, 0xaf, 0xdc, 0xd1, 0xda, 0xa5, 0xeb, 0x98, 0x5f, 0xe9, 0xbe, 0xcf, 0x12, 0xb0, + 0xfd, 0x53, 0x28, 0xc4, 0x09, 0x82, 0x7e, 0x0c, 0xc5, 0xd1, 0x4e, 0x50, 0x54, 0x99, 0x76, 0x16, + 0xe6, 0xda, 0xd4, 0x17, 0x6d, 0x1a, 0xf7, 0x8d, 0x46, 0xe3, 0xd5, 0x3f, 0x6f, 0x5f, 0x7b, 0xf5, + 0xed, 0x6d, 0xe3, 0xef, 0xdf, 0xde, 0x36, 0xfe, 0xf8, 0xaf, 0xdb, 0xc6, 0xd7, 0xf7, 0x17, 0xfa, + 0xeb, 0x2e, 0xe1, 0xf0, 0x34, 0x2b, 0xcd, 0x0f, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0x70, 0x5e, + 0xcc, 0xaf, 0x47, 0x17, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2179,6 +2186,13 @@ func (m *Request_Open) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.SealedConfigJson) > 0 { + i -= len(m.SealedConfigJson) + copy(dAtA[i:], m.SealedConfigJson) + i = encodeVarintMaterialize(dAtA, i, uint64(len(m.SealedConfigJson))) + i-- + dAtA[i] = 0x2a + } if len(m.StateJson) > 0 { i -= len(m.StateJson) copy(dAtA[i:], m.StateJson) @@ -3511,6 +3525,10 @@ func (m *Request_Open) ProtoSize() (n int) { if l > 0 { n += 1 + l + sovMaterialize(uint64(l)) } + l = len(m.SealedConfigJson) + if l > 0 { + n += 1 + l + sovMaterialize(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5429,6 +5447,40 @@ func (m *Request_Open) Unmarshal(dAtA []byte) error { m.StateJson = []byte{} } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SealedConfigJson", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMaterialize + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMaterialize + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthMaterialize + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SealedConfigJson = append(m.SealedConfigJson[:0], dAtA[iNdEx:postIndex]...) + if m.SealedConfigJson == nil { + m.SealedConfigJson = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipMaterialize(dAtA[iNdEx:]) diff --git a/go/protocols/materialize/materialize.proto b/go/protocols/materialize/materialize.proto index 0b53e04f0c7..3786cccc08c 100644 --- a/go/protocols/materialize/materialize.proto +++ b/go/protocols/materialize/materialize.proto @@ -139,6 +139,15 @@ message Request { (gogoproto.casttype) = "encoding/json.RawMessage", json_name = "state" ]; + // Sealed (encrypted) endpoint configuration of this materialization. + // This is the SOPS-encrypted document from which the runtime derived the + // decrypted `materialization.config_json`, including any `sops` metadata stanza. + // Connectors may reuse it to emit `configUpdate`s that modify nonsensitive + // overlay fields without re-encrypting the configuration. + bytes sealed_config_json = 5 [ + (gogoproto.casttype) = "encoding/json.RawMessage", + json_name = "sealedConfig" + ]; } Open open = 4;