diff --git a/Cargo.lock b/Cargo.lock index 924a32f..516000d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2701,6 +2701,188 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "spitzli-mail" +version = "0.3.0" +dependencies = [ + "async-trait", + "axum", + "base64 0.22.1", + "ed25519-dalek", + "hex", + "hickory-resolver", + "http-body-util", + "rcgen", + "reqwest", + "rustls-pemfile", + "serde", + "serde_json", + "sha2 0.10.9", + "spitzli-mail-message", + "spitzli-mail-protocol", + "spitzli-mail-runtime", + "spitzli-plugin-kit", + "spitzli-plugin-protocol", + "spitzli-plugin-runtime", + "tempfile", + "tokio", + "tokio-postgres", + "tokio-stream", + "tonic", + "tower", + "url", + "uuid", +] + +[[package]] +name = "spitzli-mail-message" +version = "0.1.0" +source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8019f3df67dba3fed3083f22635e4725028c1b5e#8019f3df67dba3fed3083f22635e4725028c1b5e" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "idna", + "lettre", + "mail-builder", + "serde", + "serde_json", + "sha2 0.10.9", + "thiserror 2.0.21", + "unicode-normalization", + "uuid", +] + +[[package]] +name = "spitzli-mail-protocol" +version = "0.1.0" +source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8019f3df67dba3fed3083f22635e4725028c1b5e#8019f3df67dba3fed3083f22635e4725028c1b5e" +dependencies = [ + "async-trait", + "base64 0.22.1", + "ed25519-dalek", + "hex", + "prost", + "semver", + "serde", + "serde_json", + "sha2 0.10.9", + "spitzli-plugin-runtime", + "thiserror 2.0.21", + "tokio", + "tokio-stream", + "tonic", + "tonic-prost", + "tonic-prost-build", + "uuid", +] + +[[package]] +name = "spitzli-mail-runtime" +version = "0.1.0" +source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8019f3df67dba3fed3083f22635e4725028c1b5e#8019f3df67dba3fed3083f22635e4725028c1b5e" +dependencies = [ + "aes-gcm", + "async-trait", + "axum", + "base64 0.22.1", + "chrono", + "ed25519-dalek", + "futures-util", + "getrandom 0.2.17", + "hex", + "hmac 0.12.1", + "http-body-util", + "mail-send", + "reqwest", + "rustix", + "rustls", + "serde", + "serde_json", + "sha2 0.10.9", + "smtp-proto", + "spitzli-mail-message", + "spitzli-mail-protocol", + "spitzli-plugin-runtime", + "thiserror 2.0.21", + "tokio", + "tokio-rustls", + "tokio-stream", + "tonic", + "tower", + "url", + "uuid", + "x509-parser", +] + +[[package]] +name = "spitzli-plugin-kit" +version = "0.1.0" +source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8019f3df67dba3fed3083f22635e4725028c1b5e#8019f3df67dba3fed3083f22635e4725028c1b5e" +dependencies = [ + "aes-gcm", + "getrandom 0.2.17", + "hex", + "rustix", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "spitzli-plugin-protocol", + "spitzli-plugin-runtime", + "tokio", + "tokio-postgres", + "tokio-postgres-rustls", + "tonic", + "url", + "uuid", +] + +[[package]] +name = "spitzli-plugin-protocol" +version = "0.1.0" +source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8019f3df67dba3fed3083f22635e4725028c1b5e#8019f3df67dba3fed3083f22635e4725028c1b5e" +dependencies = [ + "base64 0.22.1", + "chrono", + "ed25519-dalek", + "jsonschema", + "node-semver", + "regex", + "serde", + "serde_json", + "sha2 0.10.9", + "url", + "uuid", +] + +[[package]] +name = "spitzli-plugin-runtime" +version = "0.1.0" +source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8019f3df67dba3fed3083f22635e4725028c1b5e#8019f3df67dba3fed3083f22635e4725028c1b5e" +dependencies = [ + "ed25519-dalek", + "hex", + "hyper-util", + "prost", + "rustix", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "sha2 0.10.9", + "spitzli-plugin-protocol", + "tokio", + "tokio-rustls", + "tokio-stream", + "tonic", + "tonic-prost", + "tonic-prost-build", + "tower", + "url", + "x509-parser", +] + [[package]] name = "spki" version = "0.7.3" @@ -3462,188 +3644,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webdock-mail" -version = "0.3.0" -dependencies = [ - "async-trait", - "axum", - "base64 0.22.1", - "ed25519-dalek", - "hex", - "hickory-resolver", - "http-body-util", - "rcgen", - "reqwest", - "rustls-pemfile", - "serde", - "serde_json", - "sha2 0.10.9", - "tempfile", - "tokio", - "tokio-postgres", - "tokio-stream", - "tonic", - "tower", - "url", - "uuid", - "webdock-mail-message", - "webdock-mail-protocol", - "webdock-mail-runtime", - "webdock-plugin-kit", - "webdock-plugin-protocol", - "webdock-plugin-runtime", -] - -[[package]] -name = "webdock-mail-message" -version = "0.1.0" -source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6#8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "idna", - "lettre", - "mail-builder", - "serde", - "serde_json", - "sha2 0.10.9", - "thiserror 2.0.21", - "unicode-normalization", - "uuid", -] - -[[package]] -name = "webdock-mail-protocol" -version = "0.1.0" -source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6#8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -dependencies = [ - "async-trait", - "base64 0.22.1", - "ed25519-dalek", - "hex", - "prost", - "semver", - "serde", - "serde_json", - "sha2 0.10.9", - "thiserror 2.0.21", - "tokio", - "tokio-stream", - "tonic", - "tonic-prost", - "tonic-prost-build", - "uuid", - "webdock-plugin-runtime", -] - -[[package]] -name = "webdock-mail-runtime" -version = "0.1.0" -source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6#8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -dependencies = [ - "aes-gcm", - "async-trait", - "axum", - "base64 0.22.1", - "chrono", - "ed25519-dalek", - "futures-util", - "getrandom 0.2.17", - "hex", - "hmac 0.12.1", - "http-body-util", - "mail-send", - "reqwest", - "rustix", - "rustls", - "serde", - "serde_json", - "sha2 0.10.9", - "smtp-proto", - "thiserror 2.0.21", - "tokio", - "tokio-rustls", - "tokio-stream", - "tonic", - "tower", - "url", - "uuid", - "webdock-mail-message", - "webdock-mail-protocol", - "webdock-plugin-runtime", - "x509-parser", -] - -[[package]] -name = "webdock-plugin-kit" -version = "0.1.0" -source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6#8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -dependencies = [ - "aes-gcm", - "getrandom 0.2.17", - "hex", - "rustix", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "tokio", - "tokio-postgres", - "tokio-postgres-rustls", - "tonic", - "url", - "uuid", - "webdock-plugin-protocol", - "webdock-plugin-runtime", -] - -[[package]] -name = "webdock-plugin-protocol" -version = "0.1.0" -source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6#8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -dependencies = [ - "base64 0.22.1", - "chrono", - "ed25519-dalek", - "jsonschema", - "node-semver", - "regex", - "serde", - "serde_json", - "sha2 0.10.9", - "url", - "uuid", -] - -[[package]] -name = "webdock-plugin-runtime" -version = "0.1.0" -source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6#8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -dependencies = [ - "ed25519-dalek", - "hex", - "hyper-util", - "prost", - "rustix", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "sha2 0.10.9", - "tokio", - "tokio-rustls", - "tokio-stream", - "tonic", - "tonic-prost", - "tonic-prost-build", - "tower", - "url", - "webdock-plugin-protocol", - "x509-parser", -] - [[package]] name = "webpki-root-certs" version = "1.0.11" diff --git a/Cargo.toml b/Cargo.toml index 45822b8..fa3bdcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ ["package"] -"name" = "webdock-mail" +"name" = "spitzli-mail" "version" = "0.3.0" "edition" = "2024" "rust-version" = "1.94" @@ -24,20 +24,20 @@ "version" = "0.1" "features" = ["net"] -["dependencies"."webdock-mail-runtime"] +["dependencies"."spitzli-mail-runtime"] "git" = "https://github.com/spitzli/cloud-plugin-sdk" -"rev" = "8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -"package" = "webdock-mail-runtime" +"rev" = "8019f3df67dba3fed3083f22635e4725028c1b5e" +"package" = "spitzli-mail-runtime" -["dependencies"."webdock-mail-protocol"] +["dependencies"."spitzli-mail-protocol"] "git" = "https://github.com/spitzli/cloud-plugin-sdk" -"rev" = "8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -"package" = "webdock-mail-protocol" +"rev" = "8019f3df67dba3fed3083f22635e4725028c1b5e" +"package" = "spitzli-mail-protocol" -["dependencies"."webdock-mail-message"] +["dependencies"."spitzli-mail-message"] "git" = "https://github.com/spitzli/cloud-plugin-sdk" -"rev" = "8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -"package" = "webdock-mail-message" +"rev" = "8019f3df67dba3fed3083f22635e4725028c1b5e" +"package" = "spitzli-mail-message" ["dependencies"."serde"] "version" = "1" @@ -51,20 +51,20 @@ "version" = "1" "features" = ["macros", "rt-multi-thread", "signal", "time"] -["dependencies"."webdock-plugin-protocol"] +["dependencies"."spitzli-plugin-protocol"] "git" = "https://github.com/spitzli/cloud-plugin-sdk" -"rev" = "8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -"package" = "webdock-plugin-protocol" +"rev" = "8019f3df67dba3fed3083f22635e4725028c1b5e" +"package" = "spitzli-plugin-protocol" -["dependencies"."webdock-plugin-runtime"] +["dependencies"."spitzli-plugin-runtime"] "git" = "https://github.com/spitzli/cloud-plugin-sdk" -"rev" = "8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -"package" = "webdock-plugin-runtime" +"rev" = "8019f3df67dba3fed3083f22635e4725028c1b5e" +"package" = "spitzli-plugin-runtime" -["dependencies"."webdock-plugin-kit"] +["dependencies"."spitzli-plugin-kit"] "git" = "https://github.com/spitzli/cloud-plugin-sdk" -"rev" = "8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -"package" = "webdock-plugin-kit" +"rev" = "8019f3df67dba3fed3083f22635e4725028c1b5e" +"package" = "spitzli-plugin-kit" ["dependencies"."tonic"] "version" = "0.14.6" diff --git a/Dockerfile b/Dockerfile index 33805ad..a5cd24c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,16 +6,16 @@ WORKDIR /src COPY . . # Shared only among reviewed Spitzli platform sources; never customer build code. FROM source AS check -RUN --mount=type=cache,id=spitzli-native-sdk-8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6-registry,target=/usr/local/cargo/registry,sharing=locked --mount=type=cache,id=spitzli-native-sdk-8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6-git,target=/usr/local/cargo/git,sharing=locked --mount=type=cache,id=spitzli-native-sdk-8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6-target,target=/src/target,sharing=locked cargo check --locked --all-targets --jobs 3 && cargo test --locked --all-targets --jobs 3 +RUN --mount=type=cache,id=spitzli-native-sdk-8019f3df67dba3fed3083f22635e4725028c1b5e-registry,target=/usr/local/cargo/registry,sharing=locked --mount=type=cache,id=spitzli-native-sdk-8019f3df67dba3fed3083f22635e4725028c1b5e-git,target=/usr/local/cargo/git,sharing=locked --mount=type=cache,id=spitzli-native-sdk-8019f3df67dba3fed3083f22635e4725028c1b5e-target,target=/src/target,sharing=locked cargo check --locked --all-targets --jobs 3 && cargo test --locked --all-targets --jobs 3 FROM source AS build -RUN --mount=type=cache,id=spitzli-native-sdk-8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6-registry,target=/usr/local/cargo/registry,sharing=locked --mount=type=cache,id=spitzli-native-sdk-8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6-git,target=/usr/local/cargo/git,sharing=locked --mount=type=cache,id=spitzli-native-sdk-8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6-target,target=/src/target,sharing=locked cargo build --locked --release --bins --jobs 3 && mkdir /out && install -m 0555 /src/target/release/webdock-mail /out/webdock-mail && install -m 0555 /src/target/release/webdock-mail-gateway /out/webdock-mail-gateway +RUN --mount=type=cache,id=spitzli-native-sdk-8019f3df67dba3fed3083f22635e4725028c1b5e-registry,target=/usr/local/cargo/registry,sharing=locked --mount=type=cache,id=spitzli-native-sdk-8019f3df67dba3fed3083f22635e4725028c1b5e-git,target=/usr/local/cargo/git,sharing=locked --mount=type=cache,id=spitzli-native-sdk-8019f3df67dba3fed3083f22635e4725028c1b5e-target,target=/src/target,sharing=locked cargo build --locked --release --bins --jobs 3 && mkdir /out && install -m 0555 /src/target/release/spitzli-mail /out/spitzli-mail && install -m 0555 /src/target/release/spitzli-mail-gateway /out/spitzli-mail-gateway FROM docker.io/library/debian:trixie-slim@sha256:f6b08b09a05725f9431502d3b5714f5c4576e82c1a84fa1debe0c24a7e0c3ec4 COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY LICENSE COMMERCIAL-LICENSING.md NOTICE /usr/share/licenses/spitzli-cloud/ -COPY --from=build --chown=65532:65532 --chmod=0555 /out/webdock-mail /usr/local/bin/webdock-mail -COPY --from=build --chown=65532:65532 --chmod=0555 /out/webdock-mail-gateway /usr/local/bin/webdock-mail-gateway +COPY --from=build --chown=65532:65532 --chmod=0555 /out/spitzli-mail /usr/local/bin/spitzli-mail +COPY --from=build --chown=65532:65532 --chmod=0555 /out/spitzli-mail-gateway /usr/local/bin/spitzli-mail-gateway ENV HOME=/tmp USER 65532:65532 -ENTRYPOINT ["/usr/local/bin/webdock-mail"] +ENTRYPOINT ["/usr/local/bin/spitzli-mail"] diff --git a/NAMESPACE-MIGRATION.json b/NAMESPACE-MIGRATION.json new file mode 100644 index 0000000..d3b9ece --- /dev/null +++ b/NAMESPACE-MIGRATION.json @@ -0,0 +1,79 @@ +{ + "baseSource": "69890bf0c75a86baa6b9e847a5db2f8d261522d8", + "date": "2026-11-12", + "formatVersion": 1, + "licenseUnchanged": true, + "originalSourceSnapshotRetained": true, + "package": "spitzli-mail", + "preservedBindingFilesSHA256": { + "COMMERCIAL-LICENSING.md": "5b8832f5014f16e2cf4f7ee295de32760eed20981495d184bd9f1517be256a09", + "EXPORT-PROVENANCE.json": "fa706cfe2a6d39456e70c416c253921ce3ffc1f69facd4db0a12bea19c0de5ac", + "LICENSE": "0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0", + "manifest.json": "d8787b2c2341c5365efa14cbf16cf7992c7a817386b8b263dbcd229d32f231e7" + }, + "qualifiedChangedSourceSHA256": { + "Cargo.lock": "e1027e466be90f243a0b008d58411117622e3305675a8d0bba0de7b92fe4fba8", + "Cargo.toml": "054c56cd41363557f2fd7ba95d3d014be6499e2d7c952e3ab260fdaf2c6f8056", + "Dockerfile": "1c986cdc41d4dfa3c3336875e38b1c336577ce075bb5c15b96310a719691b997", + "NOTICE": "4949ccd63735c148d2fd1f1423772d11938087ba0bdbcf4aad81c576bcfa09b8", + "README.md": "8b37679e7daa6b6e2747a4a4ed7164676e42b82066d6388e563f9968ac5c1502", + "scripts/plugin-release/release.mjs": "30462a348f238bc78dab840688199574ad4fc5e9b039310306d6210f4c433890", + "src/bin/webdock-mail-gateway.rs": "4fc80c5e43492bdff80e2ecb0640c946fd1a2452a5975e83a86a2de6f5d8d775", + "src/delivery.rs": "3edcc56dfbbeb57b1aa7b0d0dc64326b7432c45f6ee6e8f8f06d86d53a8f13a0", + "src/engine.rs": "c4b8b4ef8be3395631379020b647cbde4368a9c9bdfa80f542472b71555ef0a1", + "src/executor.rs": "0e3ba4c716dd2bc47a0ec5b2965bab62fcc00ee664dbf1f9badcee938836a03f", + "src/lib.rs": "e42b1a1e2af56955cf14826b22cf6cce68ecce572471b63dd6738ee82bbc15a5", + "src/listener.rs": "dcc1ce13c9096482d8e1a7d82e3c7313ef7c76f7b05bbbf4fb4256b993e9478c", + "src/mailboxes/recovery_tests.rs": "a023b9b74c579324ad62b74292ba14c085368d6e4cb24f09a7a582a5841ce841", + "src/main.rs": "1db3874b25f12611f809b250808d5b0deb114411fd02b42655d5efcf2c4b4de3", + "src/payload.rs": "36343b30b9405d0dae0fb4de90f0b0118b3504666ca7dc6cc3ceb15461204df2", + "src/recovery.rs": "03ee3095e2a6bd0f99fc4e08daa691baeb9f28b47b2bf8637aa90342b597590b", + "src/recovery/tests.rs": "6f8658e118e40ecce4e27387c2a54f6f01b1657c5e50c37ffa483fee7bed83c9", + "src/startup.rs": "46d749c7f0bc04bc1d8e2f84c5dbfac3922ecf3796237d6c2de049ce977571da", + "src/startup_tests.rs": "02e93c53e2bab6e513d109d6e938db13854bc72646da943656504b3c5a959898", + "src/store.rs": "d92f93f49e3c5470538ed7ff94410e74eb3cbda1021fc086bf73e5474f7eb5aa", + "src/transport.rs": "0e55c3aebd74b7fa9ffe7343947dbd6b9a5e306fb85c867376aa7e4e6b8c5518", + "standalone.json": "76b8725acfca59da690678a6e7a1033d098e1b105380f03a661006de22209574", + "tests/customer_isolation.rs": "b4806472c9836fcef7da7d736111b9a856562a5a960d22a6afd9eb8020ca1a06", + "tests/fixtures/mailbox-recovery.py": "fc775ad55fce4d5c9cf449aa5dbf060049f8fa6d7e87ab674e3c3cda558694bb", + "tests/mailboxes.rs": "a1686ea5a3308c1b369f16c81e40cf62ba42c6616d8f4534bde2c524508c7de0", + "tests/managed_actions.rs": "2fd4f3c906a02082accd70d49495bcf113db97bdccfc760b08f006e27dd38c5c", + "tests/managed_contract.rs": "95ae58df5e656929d5dab2b3915badc4cd08b25db3824d7d20aebe486a8082dd", + "tests/managed_process.rs": "1cb34cef63fae60033266cbccfac7ea171d844407b9876fc62c6cc975fe3e93c", + "tests/managed_transport.rs": "4b2f7a55272b8c0466edbfe7c9d4a1525f09f728167a523499fa725a325e58c1", + "tests/private_payload.rs": "eeffe18c833eab739fd786a9f98ccbca50bd9c6c16e4d6b5624241f2c5bdd024", + "tests/reconciliation.rs": "6138f2f8d4f78dbc6b5b62c38b5e0a5a2c54c54749be66f0ef33ccfd8e9ebff4", + "tests/recovery.rs": "98dd576aa4a304461fdc3997b4d90a68cc233aae7520c2df4d7ba19a333adeda", + "tests/sending.rs": "601e4e7c1b91942b4dc46e613a59a019cfe754c5b902e8bc772e041b7a425057", + "tests/startup_diagnostics.rs": "16b44ffe24c3bb6b0893bafa77765d70e5f42333440faf1902787c27b8b9a8c2", + "tests/storage_transport.rs": "4d90318e8eb6f3af9e3471a8cf9125c975d08662b9e1510b9102904e126af9b8", + "tools/manifest-validator/Cargo.lock": "ae2cb28761abf9ce99c9f5c4df246d47d61955a9919907fd222f511d13d84ba6", + "tools/manifest-validator/Cargo.toml": "81aa8724804a7f701f7b312c8851937369409d029071d2034dbd6f535b86df6a", + "tools/manifest-validator/src/main.rs": "bb5f5ab38c63e2c28aa64a329696e923aa747e62de2d058e2de734ada06e72b4", + "ui/Send.tsx": "7f72232edc730ba9ff4d69e1ee1e017743b0e14783fd2c1f0ed422de13906192", + "ui/index.tsx": "cd7c50a1555e1c340f3074ac1a22a6e06029ab0eaa25df056cc23bba7bee4a65" + }, + "runtimeArtifactsReplaced": false, + "scope": "Rust package/import and binary namespace, host UI import namespace, paired test environment variables and exact SDK pin", + "sdkGit": "https://github.com/spitzli/cloud-plugin-sdk", + "sdkRevision": "8019f3df67dba3fed3083f22635e4725028c1b5e", + "sourceChecks": { + "allRegistryLockfilesPreserved": [ + "Cargo.lock", + "tools/manifest-validator/Cargo.lock" + ], + "format": true, + "releaseContract": true, + "rustLiteralFilesChecked": 29, + "strictClippy": true, + "testsIgnored": 28, + "testsPassed": 32, + "ui": { + "component": "mail", + "hostConsoleSource": "a9fa78a6264a64a5e3e9405a49d94da189f7638b", + "typecheckPassed": true + } + }, + "version": "0.3.0", + "wireStoredCryptographicAndExternalResourceBindings": "retained for coordinated migration" +} diff --git a/NOTICE b/NOTICE index 8f8f4fb..69c129f 100644 --- a/NOTICE +++ b/NOTICE @@ -1,7 +1,7 @@ -Webdock +Spitzli Cloud Copyright (c) 2026 Spitzli Development and contributors. -Original Webdock code is licensed under AGPL-3.0-only unless separately noted. +Original Spitzli Cloud code is licensed under AGPL-3.0-only unless separately noted. Alternative written licenses may be available from the relevant rights holder. See LICENSE and COMMERCIAL-LICENSING.md. diff --git a/README.md b/README.md index 785137a..eb69e9c 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ cargo test --manifest-path Cargo.toml # Disposable loopback PostgreSQL for plugin storage; SMTP and turboSMTP are in-test stand-ins. # Mailboxes test two isolated customer engines, cross-project reuse, retained suspend/resume and restart reconciliation # against real Stalwart CE on local dev k3s, applied by an in-test stand-in for Core: -WEBDOCK_MAIL_PLUGIN_TEST_ADMIN_URL=postgres://… WEBDOCK_WORKLOAD_TEST_KUBECONFIG=$PWD/.dev-local/k3s/kubeconfig.yaml \ +SPITZLI_MAIL_PLUGIN_TEST_ADMIN_URL=postgres://… SPITZLI_WORKLOAD_TEST_KUBECONFIG=$PWD/.dev-local/k3s/kubeconfig.yaml \ cargo test --manifest-path Cargo.toml -- --include-ignored ``` @@ -114,7 +114,7 @@ only the durable send/outbox capability; it never falls back to inline delivery. Core is the only queue and budget ledger. Private PostgreSQL stores immutable bytes, length/hash and a key sentinel with no domain/customer cascade foreign keys. Retain `secretKeyHex`: a purpose-separated SHA256-derived payload key uses the native v1 -cipher/AAD `webdock-mail:v1:payload::`. This does not reinterpret +cipher/AAD `spitzli-mail:v1:payload::`. This does not reinterpret or rotate existing relay, mailbox or engine credential ciphertexts. A wrong retained key or corrupt/mismatched payload fails closed. Pending/unknown payloads require Core reconciliation; deleting a domain does not delete their bytes. @@ -181,7 +181,7 @@ operator prerequisite. ## Native public gateway and cutover -`webdock-mail-gateway` is packaged alongside the plugin executable. Its private JSON +`spitzli-mail-gateway` is packaged alongside the plugin executable. Its private JSON has `listen` (HTTP ingress socket), `runtime` (the existing strict CoreTls shape with the runtime's HTTPS origin/domain, separate gateway certificate/key, CA and runtime leaf pin), and `preauthentication:{origin,secretFile,authority}` for the existing Core diff --git a/scripts/plugin-release/release.mjs b/scripts/plugin-release/release.mjs index 11b4590..4f227e7 100644 --- a/scripts/plugin-release/release.mjs +++ b/scripts/plugin-release/release.mjs @@ -78,7 +78,7 @@ export function validateDockerfile(raw) { export function validateSDKDependencies(dependencies) { const sources = new Set(); - for (const dependency of dependencies.filter(dep => dep.name.startsWith('webdock-'))) { + for (const dependency of dependencies.filter(dep => dep.name.startsWith('spitzli-') || dep.name.startsWith('webdock-'))) { requireValue(typeof dependency.source === 'string' && /\?rev=[a-f0-9]{40}(?:#|$)/.test(dependency.source), 'Every SDK crate must use a reviewed Git revision'); sources.add(dependency.source.split('#')[0]); } diff --git a/src/bin/webdock-mail-gateway.rs b/src/bin/webdock-mail-gateway.rs index 2b1c893..aa2f6e0 100644 --- a/src/bin/webdock-mail-gateway.rs +++ b/src/bin/webdock-mail-gateway.rs @@ -1,7 +1,7 @@ //! Explicit operator cutover entry point. No provider/private database credentials. use serde::Deserialize; +use spitzli_plugin_kit::{read_file, transport::CoreTls}; use std::{net::SocketAddr, path::PathBuf, process::ExitCode, sync::Arc}; -use webdock_plugin_kit::{read_file, transport::CoreTls}; #[derive(Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct Configuration { @@ -16,14 +16,14 @@ struct Preauthentication { secret_file: PathBuf, authority: PathBuf, } -async fn run() -> Result<(), webdock_plugin_kit::Error> { +async fn run() -> Result<(), spitzli_plugin_kit::Error> { let mut args = std::env::args_os().skip(1); let path = PathBuf::from(args.next().ok_or("configuration path required")?); if args.next().is_some() { return Err("unexpected argument".into()); } let config: Configuration = serde_json::from_slice(&read_file(&path, true)?)?; - let transport = webdock_plugin_kit::transport::RuntimeTransport { + let transport = spitzli_plugin_kit::transport::RuntimeTransport { listen: Some(PathBuf::from("/unused/listen")), core_tls: Some(config.runtime), ..Default::default() @@ -33,12 +33,12 @@ async fn run() -> Result<(), webdock_plugin_kit::Error> { let secret = String::from_utf8(read_file(&config.preauthentication.secret_file, true)?)?; let ca = reqwest::Certificate::from_pem(&read_file(&config.preauthentication.authority, false)?)?; - let control = webdock_mail_runtime::send_control::HttpSendControl::new( + let control = spitzli_mail_runtime::send_control::HttpSendControl::new( &config.preauthentication.origin, secret.trim(), &[ca], )?; - let router = webdock_mail_runtime::gateway::Gateway::new(Arc::new(control), channel).router(); + let router = spitzli_mail_runtime::gateway::Gateway::new(Arc::new(control), channel).router(); let listener = tokio::net::TcpListener::bind(config.listen).await?; axum::serve(listener, router) .with_graceful_shutdown(async { diff --git a/src/delivery.rs b/src/delivery.rs index 1873fe9..dc8b944 100644 --- a/src/delivery.rs +++ b/src/delivery.rs @@ -1,9 +1,9 @@ //! Durable action ingress. No transport runs in an invocation future. use crate::{Plugin, Result, invalid, store_error}; use serde_json::{Value, json}; +use spitzli_mail_runtime::{PrivatePayloadStore, SendControl}; +use spitzli_plugin_protocol::{ErrorCode, InvocationClaims, PluginError}; use std::sync::Arc; -use webdock_mail_runtime::{PrivatePayloadStore, SendControl}; -use webdock_plugin_protocol::{ErrorCode, InvocationClaims, PluginError}; pub struct Delivery { pub control: Arc, @@ -19,7 +19,7 @@ impl Delivery { }) } } -fn control_error(_: webdock_mail_runtime::ControlError) -> PluginError { +fn control_error(_: spitzli_mail_runtime::ControlError) -> PluginError { PluginError::new( ErrorCode::Unavailable, "Mail request could not be completed", @@ -28,8 +28,8 @@ fn control_error(_: webdock_mail_runtime::ControlError) -> PluginError { pub fn public_reply(value: &Value) -> Result { let id = value["id"] .as_str() - .filter(|s| webdock_mail_runtime::proof::valid_id(s)) - .ok_or_else(|| control_error(webdock_mail_runtime::ControlError::unavailable()))?; + .filter(|s| spitzli_mail_runtime::proof::valid_id(s)) + .ok_or_else(|| control_error(spitzli_mail_runtime::ControlError::unavailable()))?; let state = value["state"] .as_str() .filter(|s| { @@ -46,7 +46,7 @@ pub fn public_reply(value: &Value) -> Result { | "cancelled" ) }) - .ok_or_else(|| control_error(webdock_mail_runtime::ControlError::unavailable()))?; + .ok_or_else(|| control_error(spitzli_mail_runtime::ControlError::unavailable()))?; Ok(json!({"id":id,"state":state})) } impl Plugin { @@ -73,7 +73,7 @@ impl Plugin { .store .customer_fence(&claims.customer_id) .await - .map_err(|_| control_error(webdock_mail_runtime::ControlError::unavailable()))?; + .map_err(|_| control_error(spitzli_mail_runtime::ControlError::unavailable()))?; let resource = input["resource_id"] .as_str() .ok_or_else(|| invalid("Invalid mail request"))?; @@ -95,7 +95,7 @@ impl Plugin { "Verify the domain before sending", )); } - let compiled = webdock_mail_message::compile_action(input.clone(), &domain.domain) + let compiled = spitzli_mail_message::compile_action(input.clone(), &domain.domain) .map_err(|_| invalid("Invalid mail request"))?; let policy = self.private_policy(&claims.project_id).await?; let reference = uuid::Uuid::new_v4().to_string(); @@ -103,7 +103,7 @@ impl Plugin { let reply = public_reply(&admission)?; let created = admission["created"] .as_bool() - .ok_or_else(|| control_error(webdock_mail_runtime::ControlError::unavailable()))?; + .ok_or_else(|| control_error(spitzli_mail_runtime::ControlError::unavailable()))?; if !created && admission["state"] != "staging" { return Ok(reply); } @@ -113,7 +113,7 @@ impl Plugin { admission["payloadRef"] .as_str() .filter(|s| valid_uuid(s)) - .ok_or_else(|| control_error(webdock_mail_runtime::ControlError::unavailable()))? + .ok_or_else(|| control_error(spitzli_mail_runtime::ControlError::unavailable()))? .to_owned() }; if admission["state"] != "staging" @@ -122,7 +122,7 @@ impl Plugin { || !admission["uploadToken"].as_str().is_some_and(valid_uuid) { return Err(control_error( - webdock_mail_runtime::ControlError::unavailable(), + spitzli_mail_runtime::ControlError::unavailable(), )); } let lease = json!({"id":admission["id"],"customerID":claims.customer_id,"payloadRef":reference,"uploadToken":admission["uploadToken"]}); @@ -141,7 +141,7 @@ impl Plugin { .call(json!({"action":"send.abortPayload","lease":lease})) .await; return Err(control_error( - webdock_mail_runtime::ControlError::unavailable(), + spitzli_mail_runtime::ControlError::unavailable(), )); } if delivery @@ -152,7 +152,7 @@ impl Plugin { != true { return Err(control_error( - webdock_mail_runtime::ControlError::unavailable(), + spitzli_mail_runtime::ControlError::unavailable(), )); } Ok(json!({"id":admission["id"],"state":"queued"})) @@ -175,7 +175,7 @@ impl Plugin { let items = result["items"] .as_array() .filter(|i| i.len() <= 20) - .ok_or_else(|| control_error(webdock_mail_runtime::ControlError::unavailable()))?; + .ok_or_else(|| control_error(spitzli_mail_runtime::ControlError::unavailable()))?; let mut safe = Vec::with_capacity(items.len()); for item in items { let mut row = public_reply(item)?; @@ -183,7 +183,7 @@ impl Plugin { row[key] = item .get(key) .ok_or_else( - || control_error(webdock_mail_runtime::ControlError::unavailable()), + || control_error(spitzli_mail_runtime::ControlError::unavailable()), )? .clone(); } @@ -203,7 +203,7 @@ impl Plugin { != true { return Err(control_error( - webdock_mail_runtime::ControlError::unavailable(), + spitzli_mail_runtime::ControlError::unavailable(), )); } Ok(()) @@ -240,9 +240,9 @@ pub fn private_policy_digest( platform_sha256: Option, } let platform_sha256 = relay.is_none().then(|| { - webdock_mail_protocol::sha256(&serde_json::to_vec(platform).expect("private platform JSON")) + spitzli_mail_protocol::sha256(&serde_json::to_vec(platform).expect("private platform JSON")) }); - webdock_mail_protocol::sha256( + spitzli_mail_protocol::sha256( &serde_json::to_vec(&Snapshot { version: 1, selection: "project", diff --git a/src/engine.rs b/src/engine.rs index ce147de..aca3549 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -6,8 +6,8 @@ // admin password; give production an internal entry point (or run the plugin in the cluster). use serde::{Deserialize, de::DeserializeOwned}; use serde_json::{Value, json}; +pub use spitzli_plugin_kit::workloads::{HostWorkloads, Workloads}; use std::{collections::HashMap, time::Duration}; -pub use webdock_plugin_kit::workloads::{HostWorkloads, Workloads}; #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] diff --git a/src/executor.rs b/src/executor.rs index 4aebbdf..4f90bbe 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -7,28 +7,28 @@ use crate::{ }; use serde::Deserialize; use serde_json::{Value, json}; -use std::{ - sync::Arc, - time::{Duration, SystemTime, UNIX_EPOCH}, -}; -use webdock_mail_protocol::{ +use spitzli_mail_protocol::{ sha256, valid_digest, verify_lease, wire::{LeaseProof, mail_authority_client::MailAuthorityClient}, }; -use webdock_mail_runtime::{ +use spitzli_mail_runtime::{ ControlError, SendControl, dispatch::DispatchTransport, message::{DispatchMessage, PreparedMessage}, outbox::{Receipt, SendLease}, provider_transport::{ProviderBinding, SendAuthorization, metadata_bytes}, }; +use std::{ + sync::Arc, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; #[derive(Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct DeliveryConfiguration { pub native_lease_issuer_public_key_hex: String, pub provider_binding: Option, - pub gateway_peers: Vec, + pub gateway_peers: Vec, pub native_accounts: Vec, } #[derive(Deserialize)] @@ -82,10 +82,10 @@ impl Policy { for a in &accounts { a.transport.validate()?; if !customers.insert(a.customer_id.clone()) - || !webdock_mail_runtime::proof::valid_id(&a.customer_id) + || !spitzli_mail_runtime::proof::valid_id(&a.customer_id) || a.account_id .as_ref() - .is_some_and(|id| !webdock_mail_runtime::proof::valid_id(id)) + .is_some_and(|id| !spitzli_mail_runtime::proof::valid_id(id)) || [ Some(a.transport_revision), Some(a.binding_revision), @@ -136,7 +136,7 @@ impl Policy { if let Some(source) = job.source_id.strip_prefix("action:") { let Some(project) = job.source_context["projectID"] .as_str() - .filter(|p| webdock_mail_runtime::proof::valid_id(p)) + .filter(|p| spitzli_mail_runtime::proof::valid_id(p)) else { return Ok(false); }; @@ -161,7 +161,7 @@ impl Policy { Ok(job .source_id .strip_prefix("key:") - .is_some_and(webdock_mail_runtime::proof::valid_id) + .is_some_and(spitzli_mail_runtime::proof::valid_id) && self.accounts.iter().any(|a| { a.customer_id == job.customer_id && job.route["transportRevision"] == a.transport_revision @@ -254,7 +254,7 @@ impl SendControl for FencedControl { .await?; let customer = customer["customerID"] .as_str() - .filter(|s| webdock_mail_runtime::proof::valid_id(s)) + .filter(|s| spitzli_mail_runtime::proof::valid_id(s)) .ok_or_else(ControlError::unavailable)?; let _guard = self .policy diff --git a/src/lib.rs b/src/lib.rs index b625604..11ac82a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ //! Mail as a plugin: sending domains, delivery through the platform or a project's own //! turboSMTP account, and a send action exposed to the Console, the public API and MCP. //! The Core admits every call; this plugin checks again that records belong to the invoking project. -pub use webdock_plugin_kit::seal; +pub use spitzli_plugin_kit::seal; pub mod delivery; pub mod engine; pub mod executor; @@ -14,10 +14,10 @@ pub mod transport; use serde::Deserialize; use serde_json::{Value, json}; +use spitzli_plugin_protocol::{ErrorCode, InvocationClaims, Manifest, PluginError}; +use spitzli_plugin_runtime::ActionHandler; use std::sync::Arc; use uuid::Uuid; -use webdock_plugin_protocol::{ErrorCode, InvocationClaims, Manifest, PluginError}; -use webdock_plugin_runtime::ActionHandler; type Result = std::result::Result; pub const PLUGIN_ID: &str = "mail"; @@ -361,7 +361,7 @@ impl MailActionBoundary { value == Some(std::ffi::OsStr::new("1")) } fn emit(self, action: &str, code: Option) { - if Self::enabled(std::env::var_os("WEBDOCK_RECOVERY_COMPONENT_TEST").as_deref()) + if Self::enabled(std::env::var_os("SPITZLI_RECOVERY_COMPONENT_TEST").as_deref()) && let Some(line) = self.line(action, code) { use std::io::Write; @@ -585,11 +585,11 @@ mod system_dns_probe { #[ignore = "owned constructor-only probe; no DNS lookup"] async fn constructor_only() { assert_eq!( - std::env::var("WEBDOCK_RECOVERY_COMPONENT_TEST").as_deref(), + std::env::var("SPITZLI_RECOVERY_COMPONENT_TEST").as_deref(), Ok("1") ); - let metadata = std::env::var("WEBDOCK_DNS_OS_METADATA").as_deref() == Ok("1"); - let expected = match std::env::var("WEBDOCK_DNS_CONSTRUCTOR_EXPECT").as_deref() { + let metadata = std::env::var("SPITZLI_DNS_OS_METADATA").as_deref() == Ok("1"); + let expected = match std::env::var("SPITZLI_DNS_CONSTRUCTOR_EXPECT").as_deref() { Ok("ok") => Some(true), Ok("deny") => Some(false), Ok("observe") if metadata => None, diff --git a/src/listener.rs b/src/listener.rs index 1c7665b..fd607ac 100644 --- a/src/listener.rs +++ b/src/listener.rs @@ -6,8 +6,8 @@ use axum::{ middleware::{self, Next}, response::Response, }; +use spitzli_plugin_runtime::{CorePeer, MutualTls, Runtime, UnixSocket}; use std::{future::Future, sync::Arc, time::Duration}; -use webdock_plugin_runtime::{CorePeer, MutualTls, Runtime, UnixSocket}; pub fn native_routes(router: Router, peers: Vec) -> Router { let peers = Arc::new(peers); @@ -16,7 +16,7 @@ pub fn native_routes(router: Router, peers: Vec) -> Router { async move { let mut probe = tonic::Request::new(()); *probe.extensions_mut() = request.extensions().clone(); - let allowed = webdock_plugin_runtime::authenticated_peer(&probe) + let allowed = spitzli_plugin_runtime::authenticated_peer(&probe) .is_ok_and(|peer| peers.iter().any(|p| p.permits(&peer))); if !allowed { return Response::builder() diff --git a/src/mailboxes/recovery_tests.rs b/src/mailboxes/recovery_tests.rs index 2357f9f..33774e6 100644 --- a/src/mailboxes/recovery_tests.rs +++ b/src/mailboxes/recovery_tests.rs @@ -137,15 +137,15 @@ impl Drop for Fixture { impl Fixture { async fn new(outcome: Outcome) -> Self { let url = - std::env::var("WEBDOCK_MAIL_RECOVERY_TEST_URL").expect("owned_fixture_url_required"); + std::env::var("SPITZLI_MAIL_RECOVERY_TEST_URL").expect("owned_fixture_url_required"); let ca = std::fs::read( - std::env::var("WEBDOCK_MAIL_RECOVERY_TEST_CA").expect("owned_fixture_ca_required"), + std::env::var("SPITZLI_MAIL_RECOVERY_TEST_CA").expect("owned_fixture_ca_required"), ) .unwrap(); let store = Store::connect_tls(&url, &ca) .await .expect("owned_store_connect"); - let db = webdock_plugin_kit::postgres::connect(&url, Some(&ca)) + let db = spitzli_plugin_kit::postgres::connect(&url, Some(&ca)) .await .expect("owned_sql_connect"); let ownership = db.query_one("SELECT current_user::text,pg_get_userbyid(datdba)::text,(SELECT ssl FROM pg_stat_ssl WHERE pid=pg_backend_pid()) FROM pg_database WHERE datname=current_database()", &[]).await.unwrap(); @@ -257,8 +257,8 @@ impl Fixture { 1, "ambiguous_provider_outcome_lost_durable_custody" ); - let url = std::env::var("WEBDOCK_MAIL_RECOVERY_TEST_URL").unwrap(); - let ca = std::fs::read(std::env::var("WEBDOCK_MAIL_RECOVERY_TEST_CA").unwrap()).unwrap(); + let url = std::env::var("SPITZLI_MAIL_RECOVERY_TEST_URL").unwrap(); + let ca = std::fs::read(std::env::var("SPITZLI_MAIL_RECOVERY_TEST_CA").unwrap()).unwrap(); let store = Store::connect_tls(&url, &ca).await.unwrap(); let state = store.recovery_state().await.unwrap(); let operation = Uuid::new_v4(); @@ -312,7 +312,7 @@ impl Fixture { #[tokio::test] #[ignore = "owned TLS PostgreSQL; plugins/mail/tests/fixtures/mailbox-recovery.py"] async fn mailbox_unknown_response_retains_custody() { - let outcome = match std::env::var("WEBDOCK_MAIL_FIXTURE_OUTCOME") + let outcome = match std::env::var("SPITZLI_MAIL_FIXTURE_OUTCOME") .unwrap() .as_str() { @@ -345,7 +345,7 @@ async fn mailbox_unknown_response_retains_custody() { #[tokio::test] #[ignore = "owned TLS PostgreSQL; plugins/mail/tests/fixtures/mailbox-recovery.py"] async fn reconciliation_partial_completion_retains_custody() { - let suspend = std::env::var("WEBDOCK_MAIL_FIXTURE_OUTCOME").unwrap() == "suspend"; + let suspend = std::env::var("SPITZLI_MAIL_FIXTURE_OUTCOME").unwrap() == "suspend"; let f = Fixture::new(if suspend { Outcome::SuspendLost } else { @@ -379,7 +379,7 @@ async fn reconciliation_partial_completion_retains_custody() { async fn mailbox_success_without_metadata_persistence_retains_custody() { let f = Fixture::new(Outcome::Success).await; let mailbox = f.mailbox("101").await; - let refusal = if std::env::var("WEBDOCK_MAIL_FIXTURE_OUTCOME").unwrap() == "skip" { + let refusal = if std::env::var("SPITZLI_MAIL_FIXTURE_OUTCOME").unwrap() == "skip" { "RETURN NULL;" } else { "RAISE EXCEPTION 'fixture_metadata_failure';" @@ -518,7 +518,7 @@ async fn cancelled_mailbox_process_prepare() { f.provider.accepted().await; assert_eq!(f.count().await, 1); std::fs::write( - std::env::var("WEBDOCK_MAIL_FIXTURE_ACCEPTED").unwrap(), + std::env::var("SPITZLI_MAIL_FIXTURE_ACCEPTED").unwrap(), b"accepted", ) .unwrap(); @@ -547,7 +547,7 @@ async fn cancelled_mailbox_process_verify() { async fn reconciliation_success_without_metadata_persistence_retains_custody() { let f = Fixture::new(Outcome::Success).await; f.plugin.engine_password("101").await.unwrap(); - let refusal = if std::env::var("WEBDOCK_MAIL_FIXTURE_OUTCOME").unwrap() == "skip" { + let refusal = if std::env::var("SPITZLI_MAIL_FIXTURE_OUTCOME").unwrap() == "skip" { "RETURN NULL;" } else { "RAISE EXCEPTION 'fixture_metadata_failure';" diff --git a/src/main.rs b/src/main.rs index d45bf07..3df17d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ //! Mail runtime over private Unix sockets or pinned mTLS, with its own metadata database. use serde::Deserialize; +use spitzli_plugin_kit::transport::{CoreTls, ListenTls, RuntimeTransport}; +use spitzli_plugin_runtime::{self as runtime}; use std::{path::PathBuf, process::ExitCode, sync::Arc}; -use webdock_plugin_kit::transport::{CoreTls, ListenTls, RuntimeTransport}; -use webdock_plugin_runtime::{self as runtime}; type Error = Box; mod startup; @@ -35,7 +35,7 @@ enum StartupStage { } impl StartupStage { fn emit(self) { - if std::env::var_os("WEBDOCK_RECOVERY_COMPONENT_TEST").as_deref() + if std::env::var_os("SPITZLI_RECOVERY_COMPONENT_TEST").as_deref() == Some(std::ffi::OsStr::new("1")) { let stage = match self { @@ -83,14 +83,14 @@ struct Configuration { storage_url: String, storage_ca: Option, secret_key_hex: String, - platform_transport: webdock_mail::transport::Platform, + platform_transport: spitzli_mail::transport::Platform, #[serde(default)] trust_unverified_domains: bool, - mailboxes: Option, - delivery: Option, + mailboxes: Option, + delivery: Option, } fn certificate_leaf(path: &std::path::Path) -> Result, Error> { - rustls_pemfile::certs(&mut std::io::Cursor::new(webdock_plugin_kit::read_file( + rustls_pemfile::certs(&mut std::io::Cursor::new(spitzli_plugin_kit::read_file( path, false, )?)) .next() @@ -103,7 +103,7 @@ unsafe extern "C" { fn geteuid() -> u32; } fn report_config_read_failure(path: &std::path::Path, error: &Error) { - if std::env::var_os("WEBDOCK_RECOVERY_COMPONENT_TEST").as_deref() + if std::env::var_os("SPITZLI_RECOVERY_COMPONENT_TEST").as_deref() != Some(std::ffi::OsStr::new("1")) { return; @@ -149,7 +149,7 @@ async fn run() -> Result<(), Error> { return Err("unexpected argument".into()); } StartupStage::ConfigRead.emit(); - let configuration_bytes = match webdock_plugin_kit::read_file(&path, true) { + let configuration_bytes = match spitzli_plugin_kit::read_file(&path, true) { Ok(bytes) => bytes, Err(error) => { report_config_read_failure(&path, &error); @@ -168,16 +168,16 @@ async fn run() -> Result<(), Error> { StartupStage::Transport.emit(); transport.validate()?; StartupStage::ManifestRead.emit(); - let manifest_bytes = webdock_plugin_kit::read_file(&config.manifest, false)?; + let manifest_bytes = spitzli_plugin_kit::read_file(&config.manifest, false)?; StartupStage::ManifestDecode.emit(); - let manifest = webdock_plugin_protocol::Manifest::parse(&manifest_bytes)?; - webdock_mail::validate_manifest(&manifest)?; + let manifest = spitzli_plugin_protocol::Manifest::parse(&manifest_bytes)?; + spitzli_mail::validate_manifest(&manifest)?; StartupStage::Platform.emit(); config.platform_transport.validate()?; if config.trust_unverified_domains && !matches!( config.platform_transport, - webdock_mail::transport::Platform::Smtp { .. } + spitzli_mail::transport::Platform::Smtp { .. } ) { return Err("unverified domains are trusted only with a loopback catcher".into()); @@ -185,20 +185,20 @@ async fn run() -> Result<(), Error> { StartupStage::Store.emit(); let store = match config.storage_ca { Some(path) => { - webdock_mail::store::Store::connect_tls( + spitzli_mail::store::Store::connect_tls( &config.storage_url, - &webdock_plugin_kit::read_file(&path, false)?, + &spitzli_plugin_kit::read_file(&path, false)?, ) .await? } - None => webdock_mail::store::Store::connect(&config.storage_url).await?, + None => spitzli_mail::store::Store::connect(&config.storage_url).await?, }; StartupStage::CoreClient.emit(); let client = transport.core_client_lazy().await?; StartupStage::Engines.emit(); let engines = match config.mailboxes { - Some(mailboxes) => Some(webdock_mail::engine::Engines::new( - Box::new(webdock_mail::engine::HostWorkloads { + Some(mailboxes) => Some(spitzli_mail::engine::Engines::new( + Box::new(spitzli_mail::engine::HostWorkloads { client: client.clone(), plugin: manifest.id().into(), generation: config.generation, @@ -208,18 +208,18 @@ async fn run() -> Result<(), Error> { None => None, }; StartupStage::Plugin.emit(); - let plugin = webdock_mail::Plugin::new( + let plugin = spitzli_mail::Plugin::new( store, { StartupStage::Sealer.emit(); - webdock_mail::seal::Sealer::new(&config.secret_key_hex)? + spitzli_mail::seal::Sealer::new(&config.secret_key_hex)? }, - webdock_mail::transport::Sender::new(webdock_mail::transport::TURBOSMTP_ORIGIN), + spitzli_mail::transport::Sender::new(spitzli_mail::transport::TURBOSMTP_ORIGIN), Box::new({ StartupStage::Dns.emit(); - webdock_mail::SystemDns::new()? + spitzli_mail::SystemDns::new()? }), - webdock_mail::Settings { + spitzli_mail::Settings { platform: config.platform_transport.clone(), trust_unverified_domains: config.trust_unverified_domains, }, @@ -243,7 +243,7 @@ async fn run() -> Result<(), Error> { if delivery.gateway_peers.len() > 16 || delivery.gateway_peers.iter().any(|peer| match peer { runtime::CorePeer::MutualTls { certificate_sha256 } => { - !webdock_mail_protocol::valid_digest(certificate_sha256) + !spitzli_mail_protocol::valid_digest(certificate_sha256) } runtime::CorePeer::Unix { .. } => false, }) @@ -258,28 +258,28 @@ async fn run() -> Result<(), Error> { } } StartupStage::Payloads.emit(); - let payloads: Arc = Arc::new( - webdock_mail::payload::PgPrivatePayloadStore::new( + let payloads: Arc = Arc::new( + spitzli_mail::payload::PgPrivatePayloadStore::new( plugin.store(), &config.secret_key_hex, ) .await?, ); StartupStage::DeliveryPolicy.emit(); - let policy = webdock_mail::executor::Policy::new( + let policy = spitzli_mail::executor::Policy::new( plugin.store(), config.platform_transport.clone(), { StartupStage::Sealer.emit(); - webdock_mail::seal::Sealer::new(&config.secret_key_hex)? + spitzli_mail::seal::Sealer::new(&config.secret_key_hex)? }, delivery.native_accounts, )?; StartupStage::NativeControl.emit(); - let control: Arc = - webdock_mail::executor::FencedControl::new( + let control: Arc = + spitzli_mail::executor::FencedControl::new( Arc::new( - webdock_mail_protocol::native_control::NativeControlClient::new( + spitzli_mail_protocol::native_control::NativeControlClient::new( client.channel(), config.generation, )?, @@ -298,7 +298,7 @@ async fn run() -> Result<(), Error> { .map(|tls| certificate_leaf(&tls.certificate)) .transpose()?; StartupStage::ProviderBinding.emit(); - let binding = webdock_mail::executor::resolve_provider_binding( + let binding = spitzli_mail::executor::resolve_provider_binding( delivery.provider_binding, config.generation, &manifest_bytes, @@ -306,16 +306,16 @@ async fn run() -> Result<(), Error> { callback_leaf.as_deref(), )?; StartupStage::ManagedTransport.emit(); - let executor = webdock_mail::executor::ManagedTransport::new( + let executor = spitzli_mail::executor::ManagedTransport::new( policy, client.channel(), binding, &delivery.native_lease_issuer_public_key_hex, - webdock_mail::transport::Sender::new(webdock_mail::transport::TURBOSMTP_ORIGIN), + spitzli_mail::transport::Sender::new(spitzli_mail::transport::TURBOSMTP_ORIGIN), config.generation, )?; - native_router = webdock_mail::listener::native_routes( - webdock_mail_runtime::send_api::SendApi::new(control.clone(), payloads.clone()) + native_router = spitzli_mail::listener::native_routes( + spitzli_mail_runtime::send_api::SendApi::new(control.clone(), payloads.clone()) .router(), delivery.gateway_peers, ); @@ -328,7 +328,7 @@ async fn run() -> Result<(), Error> { transport: Box::new(executor), }); StartupStage::DeliveryWiring.emit(); - plugin.with_delivery(webdock_mail::delivery::Delivery::new(control, payloads))? + plugin.with_delivery(spitzli_mail::delivery::Delivery::new(control, payloads))? } else { plugin }; @@ -338,7 +338,7 @@ async fn run() -> Result<(), Error> { config.generation, ed25519_dalek::VerifyingKey::from_bytes(&key)?, Arc::new(authority), - Arc::new(webdock_mail::Handler(plugin.clone())), + Arc::new(spitzli_mail::Handler(plugin.clone())), )?; let shutdown = async { #[cfg(unix)] @@ -377,7 +377,7 @@ async fn run() -> Result<(), Error> { startup::serve_delivery( |stopped| async { Ok( - webdock_mail::listener::serve_unix(runtime, native_router, socket, async { + spitzli_mail::listener::serve_unix(runtime, native_router, socket, async { let _ = stopped.await; }) .await?, @@ -385,7 +385,7 @@ async fn run() -> Result<(), Error> { }, shutdown, client, - webdock_mail::PLUGIN_ID, + spitzli_mail::PLUGIN_ID, config.generation, resume, dispatch, @@ -397,7 +397,7 @@ async fn run() -> Result<(), Error> { let listener = tokio::net::TcpListener::bind(tls.address).await?; startup::serve_delivery( |stopped| async { - Ok(webdock_mail::listener::serve_mtls( + Ok(spitzli_mail::listener::serve_mtls( runtime, native_router, listener, @@ -410,7 +410,7 @@ async fn run() -> Result<(), Error> { }, shutdown, client, - webdock_mail::PLUGIN_ID, + spitzli_mail::PLUGIN_ID, config.generation, resume, dispatch, diff --git a/src/payload.rs b/src/payload.rs index 91c2238..8bf829d 100644 --- a/src/payload.rs +++ b/src/payload.rs @@ -2,14 +2,14 @@ use crate::store::Store; use base64::{Engine, engine::general_purpose::STANDARD}; use sha2::{Digest, Sha256}; +use spitzli_mail_runtime::{Error, PrivatePayloadStore, Result, SavedPayload, secrets::MailCipher}; use std::sync::Arc; -use webdock_mail_runtime::{Error, PrivatePayloadStore, Result, SavedPayload, secrets::MailCipher}; pub fn payload_key(retained_hex: &str) -> Result { - webdock_mail_runtime::secrets::private_payload_key_from_hex(retained_hex) + spitzli_mail_runtime::secrets::private_payload_key_from_hex(retained_hex) } fn purpose(customer: &str, reference: &str) -> Result { - if !webdock_mail_runtime::proof::valid_id(customer) + if !spitzli_mail_runtime::proof::valid_id(customer) || !customer.parse::().is_ok_and(|n| n > 0) || !uuid::Uuid::parse_str(reference).is_ok_and(|v| { v.to_string() == reference @@ -65,7 +65,7 @@ impl PgPrivatePayloadStore { impl PrivatePayloadStore for PgPrivatePayloadStore { async fn put(&self, customer: &str, reference: &str, bytes: &[u8]) -> Result { let purpose = purpose(customer, reference)?; - if bytes.is_empty() || bytes.len() > webdock_mail_message::MAX_MESSAGE { + if bytes.is_empty() || bytes.len() > spitzli_mail_message::MAX_MESSAGE { return Err(Error::Invalid); } let permit = self @@ -109,7 +109,7 @@ impl PrivatePayloadStore for PgPrivatePayloadStore { let encoded: String = cipher.open(&sealed, &purpose)?; let data = STANDARD.decode(&encoded).map_err(|_| Error::Secret)?; if data.is_empty() - || data.len() > webdock_mail_message::MAX_MESSAGE + || data.len() > spitzli_mail_message::MAX_MESSAGE || data.len() != len as usize || hex::encode(Sha256::digest(&data)) != digest || STANDARD.encode(&data) != encoded diff --git a/src/recovery.rs b/src/recovery.rs index bd2b124..613df03 100644 --- a/src/recovery.rs +++ b/src/recovery.rs @@ -3,7 +3,7 @@ use crate::store::Store; use tokio_postgres::Client; use uuid::Uuid; -type Result = std::result::Result; +type Result = std::result::Result; #[derive(Clone, Debug, PartialEq, Eq)] pub struct RecoveryState { diff --git a/src/recovery/tests.rs b/src/recovery/tests.rs index d15a112..4e3a96d 100644 --- a/src/recovery/tests.rs +++ b/src/recovery/tests.rs @@ -3,8 +3,8 @@ use super::*; #[tokio::test] #[ignore = "owned TLS PostgreSQL; plugins/mail/tests/fixtures/mailbox-recovery.py"] async fn wrong_token_and_stale_epoch_cannot_release_custody() { - let url = std::env::var("WEBDOCK_MAIL_RECOVERY_TEST_URL").unwrap(); - let ca = std::fs::read(std::env::var("WEBDOCK_MAIL_RECOVERY_TEST_CA").unwrap()).unwrap(); + let url = std::env::var("SPITZLI_MAIL_RECOVERY_TEST_URL").unwrap(); + let ca = std::fs::read(std::env::var("SPITZLI_MAIL_RECOVERY_TEST_CA").unwrap()).unwrap(); let store = Store::connect_tls(&url, &ca).await.unwrap(); let held = store.recovery_effect().await.unwrap(); let token = held.token; diff --git a/src/startup.rs b/src/startup.rs index 6f4e294..07b8bbf 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -1,6 +1,6 @@ //! Listener-scoped retained reconciliation: candidates can be probed without customer effects. +use spitzli_plugin_runtime::HostClient; use std::{future::Future, time::Duration}; -use webdock_plugin_runtime::HostClient; #[derive(Clone, Copy)] pub(super) enum LifetimeEvent { @@ -33,7 +33,7 @@ impl LifetimeEvent { pub(super) fn emit(self) { use std::io::Write; if let Some(line) = - self.line(std::env::var_os("WEBDOCK_RECOVERY_COMPONENT_TEST").as_deref()) + self.line(std::env::var_os("SPITZLI_RECOVERY_COMPONENT_TEST").as_deref()) { let _ = std::io::stderr().write_all(line.as_bytes()); } @@ -115,12 +115,12 @@ async fn resume_when_authorized( mod tests; pub(super) struct Dispatcher { - pub control: std::sync::Arc, - pub payloads: std::sync::Arc, + pub control: std::sync::Arc, + pub payloads: std::sync::Arc, #[cfg(not(test))] - pub transport: webdock_mail::executor::ManagedTransport, + pub transport: spitzli_mail::executor::ManagedTransport, #[cfg(test)] - pub transport: Box, + pub transport: Box, } /// One listener-owned sender future. Shutdown prevents a new claim and bounds the in-flight /// attempt/receipt drain; dropping it after handoff leaves Core's conservative unknown state. @@ -168,7 +168,7 @@ where if authorized && !*stopped.borrow() { // This future is deliberately retained through shutdown for bounded finalization. let _ = - webdock_mail_runtime::dispatch::dispatch_one(&*control, payloads.clone(), { + spitzli_mail_runtime::dispatch::dispatch_one(&*control, payloads.clone(), { #[cfg(not(test))] { &transport @@ -183,7 +183,7 @@ where return; } let _ = - webdock_mail_runtime::dispatch::cleanup_payloads(&*control, payloads.clone()) + spitzli_mail_runtime::dispatch::cleanup_payloads(&*control, payloads.clone()) .await; } tokio::select! {biased;_=stopped.changed()=>return,()=tokio::time::sleep(Duration::from_millis(250))=>{}} diff --git a/src/startup_tests.rs b/src/startup_tests.rs index bc0a5fb..2c024bd 100644 --- a/src/startup_tests.rs +++ b/src/startup_tests.rs @@ -1,3 +1,4 @@ +use spitzli_plugin_runtime::{HostClient, UnixSocket, wire}; use std::{ os::unix::fs::MetadataExt, path::PathBuf, @@ -9,7 +10,6 @@ use std::{ }; use tokio::sync::{mpsc, oneshot}; use tonic::{Request, Response, Status}; -use webdock_plugin_runtime::{HostClient, UnixSocket, wire}; #[derive(Clone)] struct Host { @@ -390,7 +390,7 @@ async fn shutdown_prevents_resume_when_callback_grants_during_real_graceful_drai )); f.attempt().await; let mut draining_client = wire::plugin_host_client::PluginHostClient::new( - webdock_plugin_runtime::unix_channel(path).await.unwrap(), + spitzli_plugin_runtime::unix_channel(path).await.unwrap(), ); let request = tokio::spawn(async move { draining_client @@ -440,14 +440,14 @@ async fn shutdown_prevents_resume_when_callback_grants_during_real_graceful_drai async fn initially_offline_callback_preserves_discovery_then_resumes_when_available() { struct NoActions; #[tonic::async_trait] - impl webdock_plugin_runtime::ActionHandler for NoActions { + impl spitzli_plugin_runtime::ActionHandler for NoActions { async fn invoke( &self, - _: &webdock_plugin_protocol::InvocationClaims, + _: &spitzli_plugin_protocol::InvocationClaims, _: &str, _: serde_json::Value, _: serde_json::Value, - ) -> Result { + ) -> Result { panic!("discovery must not invoke a product action"); } } @@ -457,23 +457,23 @@ async fn initially_offline_callback_preserves_discovery_then_resumes_when_availa let core_path = f.directory.join("core.sock"); assert!(!core_path.exists()); let path = f.directory.join("plugin.sock"); - let transport = webdock_plugin_kit::transport::RuntimeTransport { + let transport = spitzli_plugin_kit::transport::RuntimeTransport { listen: Some(path.clone()), core: Some(core_path.clone()), ..Default::default() }; let client = transport.core_client_lazy().await.unwrap(); - let authority = webdock_plugin_runtime::RemoteAuthority::new( + let authority = spitzli_plugin_runtime::RemoteAuthority::new( client.clone(), "mail".into(), 2, - vec![webdock_plugin_runtime::CorePeer::Unix { + vec![spitzli_plugin_runtime::CorePeer::Unix { uid: std::fs::metadata(&f.directory).unwrap().uid(), }], ) .unwrap(); - let runtime = webdock_plugin_runtime::Runtime::new( - webdock_plugin_protocol::Manifest::parse(include_bytes!("../manifest.json")).unwrap(), + let runtime = spitzli_plugin_runtime::Runtime::new( + spitzli_plugin_protocol::Manifest::parse(include_bytes!("../manifest.json")).unwrap(), 2, ed25519_dalek::SigningKey::from_bytes(&[71; 32]).verifying_key(), Arc::new(authority), @@ -502,7 +502,7 @@ async fn initially_offline_callback_preserves_discovery_then_resumes_when_availa std::future::ready(Ok::<(), ()>(())) }, )); - let mut probe = webdock_plugin_runtime::connect_unix(path).await.unwrap(); + let mut probe = spitzli_plugin_runtime::connect_unix(path).await.unwrap(); let health = probe .health(wire::HealthRequest {}) .await @@ -683,59 +683,59 @@ fn lifetime_diagnostics_are_exact_flag_static_enums_only() { } struct LifetimeDeniedControl(AtomicUsize); #[tonic::async_trait] -impl webdock_mail_runtime::SendControl for LifetimeDeniedControl { +impl spitzli_mail_runtime::SendControl for LifetimeDeniedControl { async fn call( &self, _: serde_json::Value, - ) -> Result { + ) -> Result { self.0.fetch_add(1, Ordering::SeqCst); - Err(webdock_mail_runtime::ControlError { status: 403 }) + Err(spitzli_mail_runtime::ControlError { status: 403 }) } } struct LifetimeDeniedPayloads; #[tonic::async_trait] -impl webdock_mail_runtime::PrivatePayloadStore for LifetimeDeniedPayloads { +impl spitzli_mail_runtime::PrivatePayloadStore for LifetimeDeniedPayloads { async fn put( &self, _: &str, _: &str, _: &[u8], - ) -> webdock_mail_runtime::Result { + ) -> spitzli_mail_runtime::Result { panic!("lifetime calibration must never write a payload") } - async fn get(&self, _: &str, _: &str) -> webdock_mail_runtime::Result> { + async fn get(&self, _: &str, _: &str) -> spitzli_mail_runtime::Result> { panic!("lifetime calibration must never read a payload") } - async fn remove(&self, _: &str, _: &str) -> webdock_mail_runtime::Result<()> { + async fn remove(&self, _: &str, _: &str) -> spitzli_mail_runtime::Result<()> { panic!("lifetime calibration must never remove a payload") } } struct LifetimeDeniedTransport; #[tonic::async_trait] -impl webdock_mail_runtime::dispatch::DispatchTransport for LifetimeDeniedTransport { - fn can_submit(&self, _: &webdock_mail_runtime::outbox::SendLease) -> bool { +impl spitzli_mail_runtime::dispatch::DispatchTransport for LifetimeDeniedTransport { + fn can_submit(&self, _: &spitzli_mail_runtime::outbox::SendLease) -> bool { panic!("lifetime calibration must never prepare a provider send") } async fn submit( &self, - _: &webdock_mail_runtime::outbox::SendLease, + _: &spitzli_mail_runtime::outbox::SendLease, _: &serde_json::Value, - _: &webdock_mail_runtime::message::DispatchMessage, - _: &webdock_mail_runtime::message::PreparedMessage, - ) -> webdock_mail_runtime::outbox::Receipt { + _: &spitzli_mail_runtime::message::DispatchMessage, + _: &spitzli_mail_runtime::message::PreparedMessage, + ) -> spitzli_mail_runtime::outbox::Receipt { panic!("lifetime calibration must never send to a provider") } } struct LifetimeDeniedActions; #[tonic::async_trait] -impl webdock_plugin_runtime::ActionHandler for LifetimeDeniedActions { +impl spitzli_plugin_runtime::ActionHandler for LifetimeDeniedActions { async fn invoke( &self, - _: &webdock_plugin_protocol::InvocationClaims, + _: &spitzli_plugin_protocol::InvocationClaims, _: &str, _: serde_json::Value, _: serde_json::Value, - ) -> Result { + ) -> Result { panic!("lifetime calibration must never invoke a customer action") } } @@ -774,23 +774,23 @@ struct LifetimeTls { } impl LifetimeTls { async fn channel(&self, role: &str) -> tonic::transport::Channel { - let material = webdock_plugin_runtime::PinnedMutualTls { - certificate: webdock_plugin_kit::read_file( + let material = spitzli_plugin_runtime::PinnedMutualTls { + certificate: spitzli_plugin_kit::read_file( &self.root.join(format!("{role}.crt")), false, ) .unwrap(), - private_key: webdock_plugin_kit::read_file( + private_key: spitzli_plugin_kit::read_file( &self.root.join(format!("{role}.key")), true, ) .unwrap(), - authority: webdock_plugin_kit::read_file(&self.root.join("ca.crt"), false).unwrap(), + authority: spitzli_plugin_kit::read_file(&self.root.join("ca.crt"), false).unwrap(), server_certificate_sha256: self.pin.clone(), }; let connected = tokio::time::timeout( Duration::from_secs(3), - webdock_plugin_runtime::pinned_mtls_authenticated( + spitzli_plugin_runtime::pinned_mtls_authenticated( self.origin.clone(), "localhost".into(), material, @@ -811,7 +811,7 @@ impl LifetimeTls { assert!(description.is_ok(), "local Core Describe denied"); assert_eq!( description.unwrap().into_inner().manifest_json, - webdock_plugin_protocol::Manifest::parse(include_bytes!("../manifest.json")) + spitzli_plugin_protocol::Manifest::parse(include_bytes!("../manifest.json")) .unwrap() .as_bytes() ); @@ -834,9 +834,9 @@ impl LifetimeTls { #[tokio::test] async fn actual_delivery_listener_survives_gateway_401_and_authorization_transition() { + use spitzli_plugin_runtime::{CorePeer, MutualTls, RemoteAuthority, Runtime}; use std::os::unix::fs::PermissionsExt; use tower::ServiceExt; - use webdock_plugin_runtime::{CorePeer, MutualTls, RemoteAuthority, Runtime}; let mut f = Fixture::new().await; let mut ca_params = rcgen::CertificateParams::new(Vec::::new()).unwrap(); ca_params @@ -864,7 +864,7 @@ async fn actual_delivery_listener_survives_gateway_401_and_authorization_transit ]; let key = rcgen::KeyPair::generate().unwrap(); let cert = params.signed_by(&key, &issuer).unwrap(); - pins.insert(role, webdock_mail_protocol::sha256(cert.der())); + pins.insert(role, spitzli_mail_protocol::sha256(cert.der())); for (suffix, bytes) in [ ("crt", cert.pem().into_bytes()), ("key", key.serialize_pem().into_bytes()), @@ -894,7 +894,7 @@ async fn actual_delivery_listener_survives_gateway_401_and_authorization_transit ) .unwrap(); let runtime = Runtime::new( - webdock_plugin_protocol::Manifest::parse(include_bytes!("../manifest.json")).unwrap(), + spitzli_plugin_protocol::Manifest::parse(include_bytes!("../manifest.json")).unwrap(), 2, ed25519_dalek::SigningKey::from_bytes(&[71; 32]).verifying_key(), Arc::new(authority), @@ -903,8 +903,8 @@ async fn actual_delivery_listener_survives_gateway_401_and_authorization_transit .unwrap(); let control = Arc::new(LifetimeDeniedControl(AtomicUsize::new(0))); let payloads = Arc::new(LifetimeDeniedPayloads); - let router = webdock_mail::listener::native_routes( - webdock_mail_runtime::send_api::SendApi::new(control.clone(), payloads.clone()).router(), + let router = spitzli_mail::listener::native_routes( + spitzli_mail_runtime::send_api::SendApi::new(control.clone(), payloads.clone()).router(), vec![CorePeer::MutualTls { certificate_sha256: pins["gateway"].clone(), }], @@ -928,7 +928,7 @@ async fn actual_delivery_listener_survives_gateway_401_and_authorization_transit stop: Some(stop), task: tokio::spawn(super::serve_delivery( |drain| async move { - Ok(webdock_mail::listener::serve_mtls( + Ok(spitzli_mail::listener::serve_mtls( runtime, router, listener, diff --git a/src/store.rs b/src/store.rs index d23be48..5cee6a2 100644 --- a/src/store.rs +++ b/src/store.rs @@ -121,10 +121,10 @@ pub struct CustomerFence { } impl Store { - pub async fn connect(url: &str) -> Result { - webdock_plugin_kit::loopback(url)?; + pub async fn connect(url: &str) -> Result { + spitzli_plugin_kit::loopback(url)?; Ok(Self( - webdock_plugin_kit::storage(url, "mail", MIGRATIONS).await?, + spitzli_plugin_kit::storage(url, "mail", MIGRATIONS).await?, Arc::new(Connection { url: url.into(), ca: None, @@ -134,9 +134,9 @@ impl Store { )) } /// Remote metadata storage verifies the actual PostgreSQL hostname with the private CA. - pub async fn connect_tls(url: &str, ca: &[u8]) -> Result { + pub async fn connect_tls(url: &str, ca: &[u8]) -> Result { Ok(Self( - webdock_plugin_kit::storage_tls(url, ca, "mail", MIGRATIONS).await?, + spitzli_plugin_kit::storage_tls(url, ca, "mail", MIGRATIONS).await?, Arc::new(Connection { url: url.into(), ca: Some(ca.into()), @@ -432,14 +432,14 @@ impl Store { pub(crate) fn recovery_slots(&self) -> Arc { self.1.recovery_slots.clone() } - pub(crate) async fn private_connection(&self) -> Result { - webdock_plugin_kit::postgres::connect(&self.1.url, self.1.ca.as_deref()).await + pub(crate) async fn private_connection(&self) -> Result { + spitzli_plugin_kit::postgres::connect(&self.1.url, self.1.ca.as_deref()).await } pub async fn customer_fence( &self, customer: &str, - ) -> Result { - if !webdock_mail_runtime::proof::valid_id(customer) { + ) -> Result { + if !spitzli_mail_runtime::proof::valid_id(customer) { return Err("invalid customer fence".into()); } let permit = tokio::time::timeout( @@ -450,7 +450,7 @@ impl Store { .map_err(|_| "customer fence deadline")? .map_err(|_| "customer fence unavailable")?; let client = - webdock_plugin_kit::postgres::connect(&self.1.url, self.1.ca.as_deref()).await?; + spitzli_plugin_kit::postgres::connect(&self.1.url, self.1.ca.as_deref()).await?; client.batch_execute("BEGIN; SET LOCAL lock_timeout='5s'; SET LOCAL statement_timeout='10s'; SET LOCAL idle_in_transaction_session_timeout='60s'").await.map_err(|_| "customer fence unavailable")?; client .query_one( diff --git a/src/transport.rs b/src/transport.rs index 3764dde..4d22e27 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -69,14 +69,14 @@ impl Sender { pub async fn submit_prepared( &self, route: Route<'_>, - job: &webdock_mail_runtime::outbox::SendLease, - message: &webdock_mail_runtime::message::DispatchMessage, - prepared: &webdock_mail_runtime::message::PreparedMessage, - ) -> webdock_mail_runtime::outbox::Receipt { - use webdock_mail_runtime::outbox::Receipt; + job: &spitzli_mail_runtime::outbox::SendLease, + message: &spitzli_mail_runtime::message::DispatchMessage, + prepared: &spitzli_mail_runtime::message::PreparedMessage, + ) -> spitzli_mail_runtime::outbox::Receipt { + use spitzli_mail_runtime::outbox::Receipt; let (key, secret) = match route { Route::Platform(Platform::Smtp { host, port }) => { - return match webdock_mail_runtime::smtp::submit_loopback( + return match spitzli_mail_runtime::smtp::submit_loopback( host, *port, &prepared.from, @@ -117,7 +117,7 @@ impl Sender { let Ok(body) = serde_json::to_vec(&body) else { return Receipt::Rejected {}; }; - if body.len() > 2 * webdock_mail_protocol::MAX_MIME_BYTES as usize + 65536 { + if body.len() > 2 * spitzli_mail_protocol::MAX_MIME_BYTES as usize + 65536 { return Receipt::Rejected {}; } let response = self diff --git a/standalone.json b/standalone.json index 98a7fb9..1432b09 100644 --- a/standalone.json +++ b/standalone.json @@ -1,15 +1,15 @@ { "binaries": [ - "webdock-mail", - "webdock-mail-gateway" + "spitzli-mail", + "spitzli-mail-gateway" ], "connectionsOwner": "Core", "coreIntegrationDependencies": [], "coreIntegrationTests": [], - "entrypoint": "webdock-mail", + "entrypoint": "spitzli-mail", "lockfileRequiresRegeneration": true, "name": "mail", - "package": "webdock-mail", + "package": "spitzli-mail", "release": { "mode": "native-extension", "protocol": "webdock.plugin.v2", @@ -18,14 +18,14 @@ "version": "0.3.0" }, "sdkDependencies": [ - "webdock-mail-message", - "webdock-mail-protocol", - "webdock-mail-runtime", - "webdock-plugin-kit", - "webdock-plugin-protocol", - "webdock-plugin-runtime" + "spitzli-mail-message", + "spitzli-mail-protocol", + "spitzli-mail-runtime", + "spitzli-plugin-kit", + "spitzli-plugin-protocol", + "spitzli-plugin-runtime" ], "sdkGit": "https://github.com/spitzli/cloud-plugin-sdk", - "sdkRevision": "8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6", + "sdkRevision": "8019f3df67dba3fed3083f22635e4725028c1b5e", "source": "plugins/mail" } diff --git a/tests/customer_isolation.rs b/tests/customer_isolation.rs index d69a0bc..73fc248 100644 --- a/tests/customer_isolation.rs +++ b/tests/customer_isolation.rs @@ -1,11 +1,11 @@ //! Real disposable PostgreSQL; host and JMAP stand-ins never send mail or touch a cluster. use serde_json::{Value, json}; +use spitzli_mail::{Dns, Handler, Plugin, Settings, engine, seal::Sealer, store::Store, transport}; +use spitzli_plugin_protocol::{ErrorCode, InvocationClaims, PluginError}; +use spitzli_plugin_runtime::ActionHandler; use std::sync::{Arc, Mutex}; use tokio_postgres::NoTls; use uuid::Uuid; -use webdock_mail::{Dns, Handler, Plugin, Settings, engine, seal::Sealer, store::Store, transport}; -use webdock_plugin_protocol::{ErrorCode, InvocationClaims, PluginError}; -use webdock_plugin_runtime::ActionHandler; #[derive(Clone, Default)] struct Host(Arc>>); @@ -49,7 +49,7 @@ struct Fixture { impl Fixture { async fn new() -> Self { let mut url = url::Url::parse( - &std::env::var("WEBDOCK_MAIL_PLUGIN_TEST_ADMIN_URL") + &std::env::var("SPITZLI_MAIL_PLUGIN_TEST_ADMIN_URL") .expect("disposable PostgreSQL required"), ) .unwrap(); diff --git a/tests/fixtures/mailbox-recovery.py b/tests/fixtures/mailbox-recovery.py index d6331fb..420aef0 100644 --- a/tests/fixtures/mailbox-recovery.py +++ b/tests/fixtures/mailbox-recovery.py @@ -75,12 +75,12 @@ def run(args): if args.case and name != args.case: continue database = 'review10_' + str(index) f.sql(container, f'CREATE DATABASE {database} OWNER mail_owner;') - env = {**os.environ, 'WEBDOCK_MAIL_RECOVERY_TEST_URL': f'postgresql://mail_owner:controlled-fixture-only@127.0.0.1:{port}/{database}', - 'WEBDOCK_MAIL_RECOVERY_TEST_CA': str(pki/'ca.crt'), 'WEBDOCK_MAIL_FIXTURE_OUTCOME': outcome} + env = {**os.environ, 'SPITZLI_MAIL_RECOVERY_TEST_URL': f'postgresql://mail_owner:controlled-fixture-only@127.0.0.1:{port}/{database}', + 'SPITZLI_MAIL_RECOVERY_TEST_CA': str(pki/'ca.crt'), 'SPITZLI_MAIL_FIXTURE_OUTCOME': outcome} command = [str(executable), '--ignored', '--exact', exact(name), '--nocapture'] if name == 'cancelled_mailbox_process_verify': marker = Path(directory)/'accepted' - env['WEBDOCK_MAIL_FIXTURE_ACCEPTED'] = str(marker) + env['SPITZLI_MAIL_FIXTURE_ACCEPTED'] = str(marker) with (Path(directory)/'cancel-private.log').open('wb') as log: child = subprocess.Popen([str(executable),'--ignored','--exact',exact('cancelled_mailbox_process_prepare')], cwd=ROOT, env=env, stdout=log, stderr=log, start_new_session=True) try: @@ -98,7 +98,7 @@ def run(args): f.inspect(container) # Label + exact-ID proof before restarting own PG only. f.docker('restart','--time','5',container) port = f.inspect(container)['NetworkSettings']['Ports']['5432/tcp'][0]['HostPort'] - env['WEBDOCK_MAIL_RECOVERY_TEST_URL'] = f'postgresql://mail_owner:controlled-fixture-only@127.0.0.1:{port}/{database}' + env['SPITZLI_MAIL_RECOVERY_TEST_URL'] = f'postgresql://mail_owner:controlled-fixture-only@127.0.0.1:{port}/{database}' until = time.monotonic()+20 while f.docker('exec',container,'pg_isready','-h','127.0.0.1','-U','postgres',allowed=True).returncode: if time.monotonic() > until: raise RuntimeError('restart_readiness_deadline') diff --git a/tests/mailboxes.rs b/tests/mailboxes.rs index 439fc1d..0a9f455 100644 --- a/tests/mailboxes.rs +++ b/tests/mailboxes.rs @@ -1,13 +1,13 @@ -//! A real Stalwart CE engine on the local dev k3s (`WEBDOCK_WORKLOAD_TEST_KUBECONFIG`, context +//! A real Stalwart CE engine on the local dev k3s (`SPITZLI_WORKLOAD_TEST_KUBECONFIG`, context //! `webdock-dev`) behind its Traefik, and plugin storage on a disposable local PostgreSQL -//! (`WEBDOCK_MAIL_PLUGIN_TEST_ADMIN_URL`, loopback). A stand-in for Core and the cluster's agent +//! (`SPITZLI_MAIL_PLUGIN_TEST_ADMIN_URL`, loopback). A stand-in for Core and the cluster's agent //! applies the engine's objects with kubectl in a namespace of the test's own. use serde_json::{Value, json}; +use spitzli_mail::{Dns, Handler, Plugin, Settings, engine, seal::Sealer, store::Store, transport}; +use spitzli_plugin_protocol::{ErrorCode, InvocationClaims, PluginError}; +use spitzli_plugin_runtime::ActionHandler; use std::{process::Command, sync::Mutex, time::Duration}; use tokio_postgres::NoTls; -use webdock_mail::{Dns, Handler, Plugin, Settings, engine, seal::Sealer, store::Store, transport}; -use webdock_plugin_protocol::{ErrorCode, InvocationClaims, PluginError}; -use webdock_plugin_runtime::ActionHandler; const IMAGE: &str = "docker.io/stalwartlabs/stalwart@sha256:74e5a7d55303ba525d939c6bf97ed4e010df7521f52d80afc22a815b66bd53f3"; @@ -35,7 +35,7 @@ struct Kube { } fn kubectl(args: &[&str], input: Option<&Value>) -> std::process::Output { let kubeconfig = - std::env::var("WEBDOCK_WORKLOAD_TEST_KUBECONFIG").expect("local dev kubeconfig"); + std::env::var("SPITZLI_WORKLOAD_TEST_KUBECONFIG").expect("local dev kubeconfig"); let mut command = Command::new("kubectl"); command .args(["--kubeconfig", &kubeconfig, "--context", "webdock-dev"]) @@ -168,7 +168,7 @@ impl engine::Workloads for Kube { fn admin_url() -> url::Url { let url = url::Url::parse( - &std::env::var("WEBDOCK_MAIL_PLUGIN_TEST_ADMIN_URL").expect("disposable local server"), + &std::env::var("SPITZLI_MAIL_PLUGIN_TEST_ADMIN_URL").expect("disposable local server"), ) .unwrap(); assert_eq!(url.host_str(), Some("127.0.0.1")); @@ -637,7 +637,7 @@ async fn mailbox_lifecycle() { ); // Mail clients reach IMAPS by server name and can verify the issued certificate. - let kubeconfig = std::env::var("WEBDOCK_WORKLOAD_TEST_KUBECONFIG").unwrap(); + let kubeconfig = std::env::var("SPITZLI_WORKLOAD_TEST_KUBECONFIG").unwrap(); let ca = std::path::Path::new(&kubeconfig) .parent() .unwrap() diff --git a/tests/managed_actions.rs b/tests/managed_actions.rs index d613629..505fdc3 100644 --- a/tests/managed_actions.rs +++ b/tests/managed_actions.rs @@ -1,16 +1,16 @@ //! Product action custody with a deliberate controlled Core stand-in, using owned TLS PostgreSQL. use serde_json::{Value, json}; +use spitzli_mail::{ + Dns, Handler, Plugin, Settings, delivery::Delivery, payload::PgPrivatePayloadStore, + seal::Sealer, store::Store, transport, +}; +use spitzli_mail_runtime::{ControlError, PrivatePayloadStore, SendControl}; +use spitzli_plugin_protocol::{ErrorCode, InvocationClaims}; +use spitzli_plugin_runtime::ActionHandler; use std::sync::{ Arc, Mutex, atomic::{AtomicBool, Ordering}, }; -use webdock_mail::{ - Dns, Handler, Plugin, Settings, delivery::Delivery, payload::PgPrivatePayloadStore, - seal::Sealer, store::Store, transport, -}; -use webdock_mail_runtime::{ControlError, PrivatePayloadStore, SendControl}; -use webdock_plugin_protocol::{ErrorCode, InvocationClaims}; -use webdock_plugin_runtime::ActionHandler; struct NoDns; #[tonic::async_trait] impl Dns for NoDns { @@ -83,21 +83,21 @@ impl SendControl for Control { } } #[async_trait::async_trait] -impl webdock_mail_protocol::native_control::Control for Control { +impl spitzli_mail_protocol::native_control::Control for Control { async fn call( &self, - peer: &webdock_plugin_runtime::PeerIdentity, + peer: &spitzli_plugin_runtime::PeerIdentity, generation: u64, input: Value, - ) -> Result { + ) -> Result { assert!(matches!( peer, - webdock_plugin_runtime::PeerIdentity::Unix { .. } + spitzli_plugin_runtime::PeerIdentity::Unix { .. } )); assert_eq!(generation, 2); ::call(self, input) .await - .map_err(|e| webdock_mail_protocol::native_control::ControlError { status: e.status }) + .map_err(|e| spitzli_mail_protocol::native_control::ControlError { status: e.status }) } } fn claims(action: &str) -> InvocationClaims { @@ -117,8 +117,8 @@ fn claims(action: &str) -> InvocationClaims { #[tokio::test] #[ignore = "requires fresh task3-owned TLS PostgreSQL"] async fn lost_admission_resumes_original_payload_and_transition_checks_share_customer_fence() { - let url = std::env::var("WEBDOCK_MAIL_TASK3_TEST_URL").unwrap(); - let ca = std::fs::read(std::env::var("WEBDOCK_MAIL_TASK3_TEST_CA").unwrap()).unwrap(); + let url = std::env::var("SPITZLI_MAIL_TASK3_TEST_URL").unwrap(); + let ca = std::fs::read(std::env::var("SPITZLI_MAIL_TASK3_TEST_CA").unwrap()).unwrap(); let store = Store::connect_tls(&url, &ca).await.unwrap(); let plugin = Plugin::new( store, @@ -178,9 +178,9 @@ async fn lost_admission_resumes_original_payload_and_transition_checks_share_cus .tempdir() .unwrap(); let socket_path = directory.path().join("core.sock"); - let mut socket = webdock_plugin_runtime::UnixSocket::bind(&socket_path).unwrap(); + let mut socket = spitzli_plugin_runtime::UnixSocket::bind(&socket_path).unwrap(); let incoming = socket.incoming().unwrap(); - let service = webdock_mail_protocol::native_control::ControlServer::service(control.clone()); + let service = spitzli_mail_protocol::native_control::ControlServer::service(control.clone()); let server = tokio::spawn(async move { let _socket = socket; tonic::transport::Server::builder() @@ -189,11 +189,11 @@ async fn lost_admission_resumes_original_payload_and_transition_checks_share_cus .await .unwrap(); }); - let channel = webdock_plugin_runtime::unix_channel(&socket_path) + let channel = spitzli_plugin_runtime::unix_channel(&socket_path) .await .unwrap(); let native = Arc::new( - webdock_mail_protocol::native_control::NativeControlClient::new(channel, 2).unwrap(), + spitzli_mail_protocol::native_control::NativeControlClient::new(channel, 2).unwrap(), ); let plugin = plugin .with_delivery(Delivery::new(native, payloads.clone())) @@ -229,13 +229,13 @@ async fn lost_admission_resumes_original_payload_and_transition_checks_share_cus .unwrap(); assert_eq!(receipt, json!({"id":"8811","state":"queued"})); let retained = payloads.get("8101", &first).await.unwrap(); - let request = webdock_mail_runtime::message::SendRequest::parse_stored_action( + let request = spitzli_mail_runtime::message::SendRequest::parse_stored_action( serde_json::from_slice(&retained).unwrap(), ) .unwrap(); let compiled = request.compile_action().unwrap(); assert!( - webdock_mail_runtime::message::prepare_action_message( + spitzli_mail_runtime::message::prepare_action_message( &compiled.raw, &compiled.sender, &compiled.to @@ -356,7 +356,7 @@ async fn lost_admission_resumes_original_payload_and_transition_checks_share_cus store.engine_enabled("8301", false).await.unwrap(); store.engine_state("8301", "suspended").await.unwrap(); assert!(!store.direct_allowed("8301").await.unwrap()); - let db = webdock_plugin_kit::postgres::connect(&url, Some(&ca)) + let db = spitzli_plugin_kit::postgres::connect(&url, Some(&ca)) .await .unwrap(); let legacy = uuid::Uuid::new_v4(); diff --git a/tests/managed_contract.rs b/tests/managed_contract.rs index 562e170..3a5fa7e 100644 --- a/tests/managed_contract.rs +++ b/tests/managed_contract.rs @@ -1,5 +1,5 @@ use serde_json::json; -use webdock_mail::{ +use spitzli_mail::{ delivery::{private_policy_digest, public_reply}, executor::DeliveryConfiguration, transport::Platform, @@ -67,17 +67,17 @@ fn strict_delivery_config_has_explicit_native_issuer_and_account_selection() { #[test] fn binding_derives_only_normal_mtls_and_validates_explicit_enrollment() { - use webdock_mail::executor::resolve_provider_binding; + use spitzli_mail::executor::resolve_provider_binding; let raw = b"exact raw manifest bytes"; let derived = resolve_provider_binding(None, 2, raw, Some(b"own leaf"), Some(b"own leaf")).unwrap(); assert_eq!( derived.descriptor_sha256, - webdock_mail_protocol::sha256(raw) + spitzli_mail_protocol::sha256(raw) ); assert_eq!( derived.worker_credential_sha256, - webdock_mail_protocol::sha256(b"own leaf") + spitzli_mail_protocol::sha256(b"own leaf") ); assert_eq!(derived.worker_id, "mail"); assert!(resolve_provider_binding(None, 2, raw, None, None).is_err()); diff --git a/tests/managed_process.rs b/tests/managed_process.rs index 0bfcc76..5a37964 100644 --- a/tests/managed_process.rs +++ b/tests/managed_process.rs @@ -4,6 +4,13 @@ use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD}; use ed25519_dalek::Signer; use http_body_util::BodyExt; use serde_json::{Value, json}; +use spitzli_mail_protocol::{ + LeaseClaims, + native_control::{Control, ControlError, ControlServer}, + outbox::SendLease, + wire as mail, +}; +use spitzli_plugin_runtime::{PeerIdentity, PinnedMutualTls, wire}; use std::{ os::unix::fs::PermissionsExt, process::Stdio, @@ -19,13 +26,6 @@ use tokio::{ }; use tonic::{Request, Response, Status}; use tower::ServiceExt; -use webdock_mail_protocol::{ - LeaseClaims, - native_control::{Control, ControlError, ControlServer}, - outbox::SendLease, - wire as mail, -}; -use webdock_plugin_runtime::{PeerIdentity, PinnedMutualTls, wire}; struct Row { job: Option, @@ -110,7 +110,7 @@ impl Control for State { v: Value, ) -> Result { if generation != 2 - || !matches!(peer,PeerIdentity::MutualTls{certificates} if certificates.first().is_some_and(|c|webdock_mail_protocol::sha256(c)==self.credential)) + || !matches!(peer,PeerIdentity::MutualTls{certificates} if certificates.first().is_some_and(|c|spitzli_mail_protocol::sha256(c)==self.credential)) { return Err(ControlError { status: 403 }); } @@ -130,12 +130,12 @@ impl Control for State { "send.admitAction" => { let a=&v["submission"]; let token=a["invocationToken"].as_str().unwrap(); - let claims=webdock_plugin_protocol::verify_invocation(token,&self.issuer.verifying_key(),"mail",2,now()).unwrap(); + let claims=spitzli_plugin_protocol::verify_invocation(token,&self.issuer.verifying_key(),"mail",2,now()).unwrap(); assert_eq!(claims.action,"mail.send");assert!(claims.job.is_none()); - let compiled=webdock_mail_message::compile_action(a["input"].clone(),&self.action_domain).unwrap(); - let proposed=json!({"payloadRef":a["payloadRef"],"payloadBytes":compiled.payload.len(),"requestHash":webdock_mail_protocol::sha256(&compiled.payload),"envelope":compiled.envelope}); + let compiled=spitzli_mail_message::compile_action(a["input"].clone(),&self.action_domain).unwrap(); + let proposed=json!({"payloadRef":a["payloadRef"],"payloadBytes":compiled.payload.len(),"requestHash":spitzli_mail_protocol::sha256(&compiled.payload),"envelope":compiled.envelope}); let source=json!({"executor":"managed-mail-v1","payloadStoreID":"mail:platform:v1","payloadStoreVersion":1,"executorGeneration":2,"projectID":claims.project_id,"resourceID":a["input"]["resource_id"],"domain":self.action_domain,"privatePolicySha256":a["privatePolicySha256"]}); - self.admit(&mut row,&proposed,format!("action:{}",webdock_mail_protocol::sha256(token.as_bytes())),source) + self.admit(&mut row,&proposed,format!("action:{}",spitzli_mail_protocol::sha256(token.as_bytes())),source) } "send.commitPayload" => { row.state = "queued".into(); @@ -210,7 +210,7 @@ impl State { payload_ref: s["payloadRef"].as_str().unwrap().into(), payload_bytes: s["payloadBytes"].as_i64().unwrap() as i32, request_hash: s["requestHash"].as_str().unwrap().into(), - payload_format: webdock_mail_protocol::PayloadFormat::Request, + payload_format: spitzli_mail_protocol::PayloadFormat::Request, source_context, route: json!({"kind":"direct","pluginID":"mail","generation":2,"transportRevision":1,"grantRevision":1,"configurationRevision":0,"descriptorSha256":self.descriptor,"bindingSha256":"d".repeat(64)}), envelope: s["envelope"].clone(), @@ -233,7 +233,7 @@ impl mail::mail_authority_server::MailAuthority for Host { &self, request: Request, ) -> Result, Status> { - let c = webdock_mail_protocol::verify_lease( + let c = spitzli_mail_protocol::verify_lease( &request.into_inner().lease_token, &self.issuer.verifying_key(), "mail", @@ -304,14 +304,14 @@ async fn channel( root: &rcgen::Certificate, pin: &rcgen::Certificate, ) -> tonic::transport::Channel { - webdock_plugin_runtime::pinned_mtls_channel( + spitzli_plugin_runtime::pinned_mtls_channel( origin, "localhost".into(), PinnedMutualTls { certificate: cert.pem().into_bytes(), private_key: key.serialize_pem().into_bytes(), authority: root.pem().into_bytes(), - server_certificate_sha256: webdock_mail_protocol::sha256(pin.der()), + server_certificate_sha256: spitzli_mail_protocol::sha256(pin.der()), }, ) .await @@ -431,7 +431,7 @@ async fn actual_mail_process_shared_endpoint_candidate_gateway_pin_receipts_and_ let (core_cert, core_key) = certificate(&issuer); let (gateway_cert, gateway_key) = certificate(&issuer); let (wrong_cert, wrong_key) = certificate(&issuer); - let descriptor = webdock_mail_protocol::sha256(include_bytes!("../manifest.json")); + let descriptor = spitzli_mail_protocol::sha256(include_bytes!("../manifest.json")); let state = Arc::new(State { row: Mutex::new(Row { job: None, @@ -449,7 +449,7 @@ async fn actual_mail_process_shared_endpoint_candidate_gateway_pin_receipts_and_ data: AtomicUsize::new(0), action_domain: format!("owned-process-{}.test", uuid::Uuid::new_v4().simple()), descriptor: descriptor.clone(), - credential: webdock_mail_protocol::sha256(runtime_cert.der()), + credential: spitzli_mail_protocol::sha256(runtime_cert.der()), issuer: ed25519_dalek::SigningKey::from_bytes(&[31; 32]), }); let smtp = TcpListener::bind("127.0.0.1:0").await.unwrap(); @@ -496,7 +496,7 @@ async fn actual_mail_process_shared_endpoint_candidate_gateway_pin_receipts_and_ let runtime_pem = write(dir.path(), "runtime.crt", runtime_cert.pem()); let runtime_key_path = write(dir.path(), "runtime.key", runtime_key.serialize_pem()); let ca_path = write(dir.path(), "ca.crt", root.pem()); - let config = json!({"manifest":manifest,"issuerPublicKeyHex":hex::encode(state.issuer.verifying_key().as_bytes()),"generation":2,"listenTls":{"address":format!("127.0.0.1:{port}"),"certificate":runtime_pem,"privateKey":runtime_key_path,"authority":ca_path},"coreTls":{"origin":format!("https://localhost:{core_port}"),"domain":"localhost","certificate":runtime_pem,"privateKey":runtime_key_path,"authority":ca_path,"serverCertificateSha256":webdock_mail_protocol::sha256(core_cert.der())},"corePeers":[{"kind":"mutualTls","certificateSha256":webdock_mail_protocol::sha256(core_cert.der())}],"storageUrl":std::env::var("WEBDOCK_MAIL_TASK3_TEST_URL").unwrap(),"storageCa":std::env::var("WEBDOCK_MAIL_TASK3_TEST_CA").unwrap(),"secretKeyHex":"24".repeat(32),"trustUnverifiedDomains":true,"platformTransport":{"kind":"smtp","host":"127.0.0.1","port":smtp_port},"delivery":{"nativeLeaseIssuerPublicKeyHex":hex::encode(state.issuer.verifying_key().as_bytes()),"providerBinding":{"pluginID":"mail","generation":2,"descriptorSha256":descriptor,"workerID":"mail","workerGeneration":2,"workerCredentialSha256":state.credential},"gatewayPeers":[{"kind":"mutualTls","certificateSha256":webdock_mail_protocol::sha256(gateway_cert.der())}],"nativeAccounts":[{"customerID":"7101","accountID":null,"mode":"managed","transportRevision":1,"bindingRevision":1,"byokRevision":null,"transport":{"kind":"smtp","host":"127.0.0.1","port":smtp_port}}]}}); + let config = json!({"manifest":manifest,"issuerPublicKeyHex":hex::encode(state.issuer.verifying_key().as_bytes()),"generation":2,"listenTls":{"address":format!("127.0.0.1:{port}"),"certificate":runtime_pem,"privateKey":runtime_key_path,"authority":ca_path},"coreTls":{"origin":format!("https://localhost:{core_port}"),"domain":"localhost","certificate":runtime_pem,"privateKey":runtime_key_path,"authority":ca_path,"serverCertificateSha256":spitzli_mail_protocol::sha256(core_cert.der())},"corePeers":[{"kind":"mutualTls","certificateSha256":spitzli_mail_protocol::sha256(core_cert.der())}],"storageUrl":std::env::var("SPITZLI_MAIL_TASK3_TEST_URL").unwrap(),"storageCa":std::env::var("SPITZLI_MAIL_TASK3_TEST_CA").unwrap(),"secretKeyHex":"24".repeat(32),"trustUnverifiedDomains":true,"platformTransport":{"kind":"smtp","host":"127.0.0.1","port":smtp_port},"delivery":{"nativeLeaseIssuerPublicKeyHex":hex::encode(state.issuer.verifying_key().as_bytes()),"providerBinding":{"pluginID":"mail","generation":2,"descriptorSha256":descriptor,"workerID":"mail","workerGeneration":2,"workerCredentialSha256":state.credential},"gatewayPeers":[{"kind":"mutualTls","certificateSha256":spitzli_mail_protocol::sha256(gateway_cert.der())}],"nativeAccounts":[{"customerID":"7101","accountID":null,"mode":"managed","transportRevision":1,"bindingRevision":1,"byokRevision":null,"transport":{"kind":"smtp","host":"127.0.0.1","port":smtp_port}}]}}); // Normal installer profile must bootstrap without a post-admission credential override. let mut config = config; config["delivery"] @@ -509,7 +509,7 @@ async fn actual_mail_process_shared_endpoint_candidate_gateway_pin_receipts_and_ serde_json::to_vec(&config).unwrap(), ); let spawn = || { - tokio::process::Command::new(env!("CARGO_BIN_EXE_webdock-mail")) + tokio::process::Command::new(env!("CARGO_BIN_EXE_spitzli-mail")) .arg(&path) .kill_on_drop(true) .stdout(Stdio::null()) @@ -605,19 +605,19 @@ async fn actual_mail_process_shared_endpoint_candidate_gateway_pin_receipts_and_ .payload_ref .clone(); let store = Arc::new( - webdock_mail::store::Store::connect_tls( - &std::env::var("WEBDOCK_MAIL_TASK3_TEST_URL").unwrap(), - &std::fs::read(std::env::var("WEBDOCK_MAIL_TASK3_TEST_CA").unwrap()).unwrap(), + spitzli_mail::store::Store::connect_tls( + &std::env::var("SPITZLI_MAIL_TASK3_TEST_URL").unwrap(), + &std::fs::read(std::env::var("SPITZLI_MAIL_TASK3_TEST_CA").unwrap()).unwrap(), ) .await .unwrap(), ); let payload = - webdock_mail::payload::PgPrivatePayloadStore::new(store.clone(), &"24".repeat(32)) + spitzli_mail::payload::PgPrivatePayloadStore::new(store.clone(), &"24".repeat(32)) .await .unwrap(); assert!( - webdock_mail_runtime::PrivatePayloadStore::get(&payload, "7101", &reference) + spitzli_mail_runtime::PrivatePayloadStore::get(&payload, "7101", &reference) .await .is_ok() ); @@ -665,7 +665,7 @@ async fn actual_mail_process_shared_endpoint_candidate_gateway_pin_receipts_and_ ); assert!(!process.wait().await.unwrap().success()); assert!( - webdock_mail_runtime::PrivatePayloadStore::get(&payload, "7101", &forced_ref) + spitzli_mail_runtime::PrivatePayloadStore::get(&payload, "7101", &forced_ref) .await .is_ok() ); diff --git a/tests/managed_transport.rs b/tests/managed_transport.rs index 72f6e5a..53fc48d 100644 --- a/tests/managed_transport.rs +++ b/tests/managed_transport.rs @@ -1,18 +1,18 @@ //! Local HTTP provider stand-in only; no provider resource, credential or real mail. use serde_json::json; +use spitzli_mail::transport::{Platform, Route, Sender}; +use spitzli_mail_runtime::{ + message::prepare_action_message, + outbox::{PayloadFormat, SendLease}, +}; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, net::TcpListener, }; -use webdock_mail::transport::{Platform, Route, Sender}; -use webdock_mail_runtime::{ - message::prepare_action_message, - outbox::{PayloadFormat, SendLease}, -}; #[tokio::test] async fn one_http_attempt_preserves_rejection_and_unknown_classifier() { let input = json!({"resource_id":"resource","from":"hello","to":"to@example.test","subject":"owned","text":"body"}); - let compiled = webdock_mail_message::compile_action(input, "example.test").unwrap(); + let compiled = spitzli_mail_message::compile_action(input, "example.test").unwrap(); let message = compiled.request.compile_action().unwrap(); let prepared = prepare_action_message(&message.raw, &message.sender, &message.to).unwrap(); let job = SendLease { @@ -21,7 +21,7 @@ async fn one_http_attempt_preserves_rejection_and_unknown_classifier() { source_id: format!("action:{}", "a".repeat(64)), payload_ref: uuid::Uuid::new_v4().to_string(), payload_bytes: compiled.payload.len() as i32, - request_hash: webdock_mail_protocol::sha256(&compiled.payload), + request_hash: spitzli_mail_protocol::sha256(&compiled.payload), payload_format: PayloadFormat::Request, source_context: json!({}), route: json!({}), diff --git a/tests/private_payload.rs b/tests/private_payload.rs index d24500a..bf4be1e 100644 --- a/tests/private_payload.rs +++ b/tests/private_payload.rs @@ -1,25 +1,25 @@ +use spitzli_mail::{payload::PgPrivatePayloadStore, store::Store}; +use spitzli_mail_runtime::PrivatePayloadStore; use std::sync::Arc; -use webdock_mail::{payload::PgPrivatePayloadStore, store::Store}; -use webdock_mail_runtime::PrivatePayloadStore; #[test] fn retained_hex_key_is_purpose_bound_and_strict() { - let a = webdock_mail::payload::payload_key(&"24".repeat(32)).unwrap(); + let a = spitzli_mail::payload::payload_key(&"24".repeat(32)).unwrap(); assert_eq!( a, - webdock_mail::payload::payload_key(&"24".repeat(32)).unwrap() + spitzli_mail::payload::payload_key(&"24".repeat(32)).unwrap() ); assert_ne!(a, "24".repeat(32)); for bad in ["", "24", &"XX".repeat(32)] { - assert!(webdock_mail::payload::payload_key(bad).is_err()); + assert!(spitzli_mail::payload::payload_key(bad).is_err()); } } #[tokio::test] #[ignore = "requires fresh task3-owned PostgreSQL"] async fn postgres_immutable_tenant_key_and_retention() { - let url = std::env::var("WEBDOCK_MAIL_TASK3_TEST_URL").unwrap(); - let ca = std::fs::read(std::env::var("WEBDOCK_MAIL_TASK3_TEST_CA").unwrap()).unwrap(); + let url = std::env::var("SPITZLI_MAIL_TASK3_TEST_URL").unwrap(); + let ca = std::fs::read(std::env::var("SPITZLI_MAIL_TASK3_TEST_CA").unwrap()).unwrap(); let store = Arc::new(Store::connect_tls(&url, &ca).await.unwrap()); let payload = PgPrivatePayloadStore::new(store.clone(), &"24".repeat(32)) .await @@ -61,7 +61,7 @@ async fn postgres_immutable_tenant_key_and_retention() { .is_err() ); assert!(payload.put("101", "bad", b"x").await.is_err()); - let domain = webdock_mail::store::Domain { + let domain = spitzli_mail::store::Domain { id: uuid::Uuid::new_v4(), domain: format!("owned-{}.test", uuid::Uuid::new_v4().simple()), token: "fixture".into(), @@ -98,8 +98,8 @@ async fn postgres_immutable_tenant_key_and_retention() { .await .is_err() ); - let ca = std::fs::read(std::env::var("WEBDOCK_MAIL_TASK3_TEST_CA").unwrap()).unwrap(); - let client = webdock_plugin_kit::postgres::connect(&url, Some(&ca)) + let ca = std::fs::read(std::env::var("SPITZLI_MAIL_TASK3_TEST_CA").unwrap()).unwrap(); + let client = spitzli_plugin_kit::postgres::connect(&url, Some(&ca)) .await .unwrap(); client diff --git a/tests/reconciliation.rs b/tests/reconciliation.rs index 649b9d7..6abb974 100644 --- a/tests/reconciliation.rs +++ b/tests/reconciliation.rs @@ -1,12 +1,12 @@ //! Interrupted setup against an in-process JMAP boundary; no mail or external services. use serde_json::{Value, json}; +use spitzli_mail::engine; +use spitzli_plugin_protocol::PluginError; use std::{ collections::BTreeMap, sync::{Arc, Mutex}, }; use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use webdock_mail::engine; -use webdock_plugin_protocol::PluginError; #[derive(Default)] struct State { diff --git a/tests/recovery.rs b/tests/recovery.rs index 7c29b9c..dccea43 100644 --- a/tests/recovery.rs +++ b/tests/recovery.rs @@ -1,14 +1,14 @@ //! Private safety interlock tests. These do not manufacture Core backup authority. -use std::sync::Arc; -use uuid::Uuid; -use webdock_mail::{ +use spitzli_mail::{ payload::PgPrivatePayloadStore, store::{Domain, Store}, }; -use webdock_mail_runtime::PrivatePayloadStore; +use spitzli_mail_runtime::PrivatePayloadStore; +use std::sync::Arc; +use uuid::Uuid; async fn connect(url: &str) -> Store { - match std::env::var("WEBDOCK_MAIL_RECOVERY_TEST_CA") { + match std::env::var("SPITZLI_MAIL_RECOVERY_TEST_CA") { Ok(path) => Store::connect_tls(url, &std::fs::read(path).unwrap()) .await .unwrap(), @@ -19,7 +19,7 @@ async fn connect(url: &str) -> Store { #[tokio::test] #[ignore = "fresh owned private DB; cancelled external effects require real reconciliation"] async fn cancelled_effect_cannot_be_mistaken_for_a_drained_transport() { - let url = std::env::var("WEBDOCK_MAIL_RECOVERY_TEST_URL").unwrap(); + let url = std::env::var("SPITZLI_MAIL_RECOVERY_TEST_URL").unwrap(); let store = connect(&url).await; let effect = store.recovery_effect().await.unwrap(); drop(effect); // A crashed/cancelled sender has no definitive completion witness. @@ -37,7 +37,7 @@ async fn cancelled_effect_cannot_be_mistaken_for_a_drained_transport() { #[tokio::test] #[ignore = "requires fresh owned PostgreSQL; tools/tests/mail-application-recovery.py"] async fn durable_gate_drains_rejects_stale_resume_and_quarantines_restore() { - let url = std::env::var("WEBDOCK_MAIL_RECOVERY_TEST_URL").unwrap(); + let url = std::env::var("SPITZLI_MAIL_RECOVERY_TEST_URL").unwrap(); let store = Arc::new(connect(&url).await); let first = store.recovery_state().await.unwrap(); let payload = PgPrivatePayloadStore::new(store.clone(), &"24".repeat(32)) @@ -131,11 +131,11 @@ async fn durable_gate_drains_rejects_stale_resume_and_quarantines_restore() { #[tokio::test] #[ignore = "owned private snapshot fixture only"] async fn private_snapshot_prepare() { - let url = std::env::var("WEBDOCK_MAIL_RECOVERY_TEST_URL").unwrap(); + let url = std::env::var("SPITZLI_MAIL_RECOVERY_TEST_URL").unwrap(); let key = - std::fs::read_to_string(std::env::var("WEBDOCK_MAIL_RECOVERY_KEY_FILE").unwrap()).unwrap(); + std::fs::read_to_string(std::env::var("SPITZLI_MAIL_RECOVERY_KEY_FILE").unwrap()).unwrap(); let store = Arc::new(connect(&url).await); - let reference = std::env::var("WEBDOCK_MAIL_RECOVERY_REFERENCE").unwrap(); + let reference = std::env::var("SPITZLI_MAIL_RECOVERY_REFERENCE").unwrap(); let payload = PgPrivatePayloadStore::new(store.clone(), &key) .await .unwrap(); @@ -155,13 +155,13 @@ async fn private_snapshot_prepare() { #[tokio::test] #[ignore = "owned fresh private restore fixture only"] async fn private_snapshot_verify() { - let url = std::env::var("WEBDOCK_MAIL_RECOVERY_TEST_URL").unwrap(); - let key_path = std::env::var("WEBDOCK_MAIL_RECOVERY_KEY_FILE").unwrap(); + let url = std::env::var("SPITZLI_MAIL_RECOVERY_TEST_URL").unwrap(); + let key_path = std::env::var("SPITZLI_MAIL_RECOVERY_KEY_FILE").unwrap(); assert!(std::fs::read_to_string(format!("{key_path}.missing")).is_err()); let key = std::fs::read_to_string(key_path).unwrap(); let store = Arc::new(connect(&url).await); - let ca = std::fs::read(std::env::var("WEBDOCK_MAIL_RECOVERY_TEST_CA").unwrap()).unwrap(); - let connection = webdock_plugin_kit::postgres::connect(&url, Some(&ca)) + let ca = std::fs::read(std::env::var("SPITZLI_MAIL_RECOVERY_TEST_CA").unwrap()).unwrap(); + let connection = spitzli_plugin_kit::postgres::connect(&url, Some(&ca)) .await .unwrap(); let ownership = connection.query_one("SELECT current_user::text,pg_get_userbyid(datdba)::text,(SELECT ssl FROM pg_stat_ssl WHERE pid=pg_backend_pid()) FROM pg_database WHERE datname=current_database()", &[]).await.unwrap(); @@ -178,7 +178,7 @@ async fn private_snapshot_verify() { let payload = PgPrivatePayloadStore::new(store.clone(), &key) .await .unwrap(); - let reference = std::env::var("WEBDOCK_MAIL_RECOVERY_REFERENCE").unwrap(); + let reference = std::env::var("SPITZLI_MAIL_RECOVERY_REFERENCE").unwrap(); assert_eq!( payload.get("101", &reference).await.unwrap(), b"owned recovery payload sentinel" diff --git a/tests/sending.rs b/tests/sending.rs index 6a97fdd..fc9e635 100644 --- a/tests/sending.rs +++ b/tests/sending.rs @@ -1,17 +1,17 @@ -//! Real plugin storage on a disposable local PostgreSQL (`WEBDOCK_MAIL_PLUGIN_TEST_ADMIN_URL`, loopback), +//! Real plugin storage on a disposable local PostgreSQL (`SPITZLI_MAIL_PLUGIN_TEST_ADMIN_URL`, loopback), //! an in-test SMTP catcher and an in-test turboSMTP stand-in. No message leaves this process. use serde_json::{Value, json}; +use spitzli_mail::{Dns, Handler, Plugin, Settings, seal::Sealer, store::Store, transport}; +use spitzli_plugin_protocol::{ErrorCode, InvocationClaims, Manifest, PluginError}; +use spitzli_plugin_runtime::ActionHandler; use std::sync::{Arc, Mutex}; use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader}; use tokio::net::TcpListener; use tokio_postgres::NoTls; -use webdock_mail::{Dns, Handler, Plugin, Settings, seal::Sealer, store::Store, transport}; -use webdock_plugin_protocol::{ErrorCode, InvocationClaims, Manifest, PluginError}; -use webdock_plugin_runtime::ActionHandler; fn admin_url() -> url::Url { let url = url::Url::parse( - &std::env::var("WEBDOCK_MAIL_PLUGIN_TEST_ADMIN_URL").expect("disposable local server"), + &std::env::var("SPITZLI_MAIL_PLUGIN_TEST_ADMIN_URL").expect("disposable local server"), ) .unwrap(); assert_eq!(url.host_str(), Some("127.0.0.1")); @@ -244,14 +244,14 @@ fn send(domain: &str, to: &str) -> Value { #[test] fn shipped_manifest_is_the_reviewed_contract() { - webdock_mail::validate_manifest(&Manifest::parse(include_bytes!("../manifest.json")).unwrap()) + spitzli_mail::validate_manifest(&Manifest::parse(include_bytes!("../manifest.json")).unwrap()) .unwrap(); } #[test] fn domain_names_are_normalised_and_bounded() { assert_eq!( - webdock_mail::domain_name(" Shop.Example.COM. ").unwrap(), + spitzli_mail::domain_name(" Shop.Example.COM. ").unwrap(), "shop.example.com" ); for bad in [ @@ -263,7 +263,7 @@ fn domain_names_are_normalised_and_bounded() { "ex ample.com", &format!("{}.com", "a".repeat(64)), ] { - assert!(webdock_mail::domain_name(bad).is_err(), "{bad}"); + assert!(spitzli_mail::domain_name(bad).is_err(), "{bad}"); } } diff --git a/tests/startup_diagnostics.rs b/tests/startup_diagnostics.rs index 6fec788..e8102dd 100644 --- a/tests/startup_diagnostics.rs +++ b/tests/startup_diagnostics.rs @@ -1,7 +1,7 @@ #[test] fn startup_failure_does_not_echo_arguments_or_underlying_diagnostics() { - let output = std::process::Command::new(env!("CARGO_BIN_EXE_webdock-mail")) - .env_remove("WEBDOCK_RECOVERY_COMPONENT_TEST") + let output = std::process::Command::new(env!("CARGO_BIN_EXE_spitzli-mail")) + .env_remove("SPITZLI_RECOVERY_COMPONENT_TEST") .arg("/fixture-private-configuration-marker") .output() .unwrap(); @@ -32,12 +32,12 @@ fn fixture_startup_markers_are_exact_and_never_echo_poison_or_private_files() { (poison, vec!["args", "config_read", "config_decode"]), ] { for mode in [None, Some("0"), Some("true"), Some("01"), Some("1")] { - let mut command = std::process::Command::new(env!("CARGO_BIN_EXE_webdock-mail")); + let mut command = std::process::Command::new(env!("CARGO_BIN_EXE_spitzli-mail")); command .arg(&path) - .env_remove("WEBDOCK_RECOVERY_COMPONENT_TEST"); + .env_remove("SPITZLI_RECOVERY_COMPONENT_TEST"); if let Some(value) = mode { - command.env("WEBDOCK_RECOVERY_COMPONENT_TEST", value); + command.env("SPITZLI_RECOVERY_COMPONENT_TEST", value); } let output = command.output().unwrap(); assert!(!output.status.success()); @@ -90,17 +90,17 @@ fn read_failure_metadata_is_scalar_only_for_real_mode_and_owner_cases() { std::fs::write(&input, b"poison-private-value").unwrap(); std::fs::set_permissions(&input, std::fs::Permissions::from_mode(0o644)).unwrap(); for path in [input, std::fs::canonicalize("/usr/bin/true").unwrap()] { - let normal = std::process::Command::new(env!("CARGO_BIN_EXE_webdock-mail")) + let normal = std::process::Command::new(env!("CARGO_BIN_EXE_spitzli-mail")) .arg(&path) - .env_remove("WEBDOCK_RECOVERY_COMPONENT_TEST") + .env_remove("SPITZLI_RECOVERY_COMPONENT_TEST") .output() .unwrap(); assert!(!normal.status.success()); assert!(normal.stdout.is_empty()); assert!(normal.stderr == b"mail_plugin_start_failed\n"); - let output = std::process::Command::new(env!("CARGO_BIN_EXE_webdock-mail")) + let output = std::process::Command::new(env!("CARGO_BIN_EXE_spitzli-mail")) .arg(&path) - .env("WEBDOCK_RECOVERY_COMPONENT_TEST", "1") + .env("SPITZLI_RECOVERY_COMPONENT_TEST", "1") .output() .unwrap(); assert!(!output.status.success()); diff --git a/tests/storage_transport.rs b/tests/storage_transport.rs index 9683ce4..b4893b5 100644 --- a/tests/storage_transport.rs +++ b/tests/storage_transport.rs @@ -1,4 +1,4 @@ -use webdock_mail::store::Store; +use spitzli_mail::store::Store; #[tokio::test] async fn plaintext_constructor_rejects_remote_and_driver_host_overrides() { diff --git a/tools/manifest-validator/Cargo.lock b/tools/manifest-validator/Cargo.lock index dd69a41..26ce02b 100644 --- a/tools/manifest-validator/Cargo.lock +++ b/tools/manifest-validator/Cargo.lock @@ -991,6 +991,32 @@ version = "1.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0d5d4b1981b8bbbb7aed204cae5726d683d5d6ac7e74ec54122ea28f315e7c8" +[[package]] +name = "spitzli-plugin-protocol" +version = "0.1.0" +source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8019f3df67dba3fed3083f22635e4725028c1b5e#8019f3df67dba3fed3083f22635e4725028c1b5e" +dependencies = [ + "base64", + "chrono", + "ed25519-dalek", + "jsonschema", + "node-semver", + "regex", + "serde", + "serde_json", + "sha2", + "url", + "uuid", +] + +[[package]] +name = "spitzli-registry-manifest-validator" +version = "0.1.0" +dependencies = [ + "serde_json", + "spitzli-plugin-protocol", +] + [[package]] name = "spki" version = "0.7.3" @@ -1239,32 +1265,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "webdock-plugin-protocol" -version = "0.1.0" -source = "git+https://github.com/spitzli/cloud-plugin-sdk?rev=8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6#8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -dependencies = [ - "base64", - "chrono", - "ed25519-dalek", - "jsonschema", - "node-semver", - "regex", - "serde", - "serde_json", - "sha2", - "url", - "uuid", -] - -[[package]] -name = "webdock-registry-manifest-validator" -version = "0.1.0" -dependencies = [ - "serde_json", - "webdock-plugin-protocol", -] - [[package]] name = "windows-link" version = "0.2.1" diff --git a/tools/manifest-validator/Cargo.toml b/tools/manifest-validator/Cargo.toml index 3d08d68..e4c3b1d 100644 --- a/tools/manifest-validator/Cargo.toml +++ b/tools/manifest-validator/Cargo.toml @@ -1,5 +1,5 @@ ["package"] -"name" = "webdock-registry-manifest-validator" +"name" = "spitzli-registry-manifest-validator" "version" = "0.1.0" "edition" = "2024" "rust-version" = "1.94" @@ -11,10 +11,10 @@ ["dependencies"] "serde_json" = "1" -["dependencies"."webdock-plugin-protocol"] +["dependencies"."spitzli-plugin-protocol"] "git" = "https://github.com/spitzli/cloud-plugin-sdk" -"rev" = "8ed57d89c3263b050f126e4c2ba7f6cdebb0daa6" -"package" = "webdock-plugin-protocol" +"rev" = "8019f3df67dba3fed3083f22635e4725028c1b5e" +"package" = "spitzli-plugin-protocol" ["profile"] diff --git a/tools/manifest-validator/src/main.rs b/tools/manifest-validator/src/main.rs index efb848a..6453517 100644 --- a/tools/manifest-validator/src/main.rs +++ b/tools/manifest-validator/src/main.rs @@ -1,5 +1,5 @@ use std::io::{self, Read, Write}; -use webdock_plugin_protocol::Manifest; +use spitzli_plugin_protocol::Manifest; const MAX_REGISTRY_BYTES: usize = 64 * 1024; diff --git a/ui/Send.tsx b/ui/Send.tsx index 7e22504..1dde2b7 100644 --- a/ui/Send.tsx +++ b/ui/Send.tsx @@ -1,7 +1,7 @@ import { useRef, useState } from "react"; import { useBlocker } from "@tanstack/react-router"; import { z } from "zod"; -import { Failure, Loading, ui, usePluginMutation, usePluginQuery, usePluginSubmission, type PluginSubmission, type ResourceProps } from "@webdock/plugin-ui"; +import { Failure, Loading, ui, usePluginMutation, usePluginQuery, usePluginSubmission, type PluginSubmission, type ResourceProps } from "@spitzli/plugin-ui"; const stateSchema = z.enum(["staging", "queued", "leased", "preparation_review", "handoff", "unknown", "accepted", "rejected", "cancelled"]); type SendState = z.infer; diff --git a/ui/index.tsx b/ui/index.tsx index 2beede5..8e99f41 100644 --- a/ui/index.tsx +++ b/ui/index.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { SendForm, Outbox } from "./Send"; import { z } from "zod"; -import { Failure, Loading, ui, usePluginMutation, usePluginQuery, usePluginReveal, type CreateProps, type PluginUI, type ResourceProps } from "@webdock/plugin-ui"; +import { Failure, Loading, ui, usePluginMutation, usePluginQuery, usePluginReveal, type CreateProps, type PluginUI, type ResourceProps } from "@spitzli/plugin-ui"; const domainSchema = z.object({ domain: z.string(),