From d3c35ec73d0ec5575b89a956b7ae660fc3a9b595 Mon Sep 17 00:00:00 2001 From: Oleh Martsokha Date: Fri, 3 Jul 2026 05:24:20 +0200 Subject: [PATCH 1/7] feat(server): add nvisy-engine + nvisy-schema dependencies Pull the runtime's detection & redaction engine into nvisy-server as git dependencies tracking main. nvisy-engine keeps its default modality features (tabular/image/audio/document); disabling them selects an uncompilable feature combination. First step toward wiring policy/context config and a redaction worker. Co-Authored-By: Claude Opus 4.8 (1M context) --- Cargo.lock | 2246 +++++++++++++++++++++++++++++++- Cargo.toml | 6 + crates/nvisy-server/Cargo.toml | 4 + 3 files changed, 2229 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd1ecee..3bc2742 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,22 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ab_glyph" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" + [[package]] name = "adler2" version = "2.0.1" @@ -62,6 +78,24 @@ dependencies = [ "syn", ] +[[package]] +name = "aligned" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" +dependencies = [ + "as-slice", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -142,6 +176,21 @@ version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + [[package]] name = "arc-swap" version = "1.9.1" @@ -151,6 +200,17 @@ dependencies = [ "rustversion", ] +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "argon2" version = "0.6.0-rc.8" @@ -163,6 +223,27 @@ dependencies = [ "password-hash", ] +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "as-any" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0f477b951e452a0b6b4a10b53ccd569042d1d01729b519e02074a9c0958a063" + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "async-compression" version = "0.4.42" @@ -211,6 +292,28 @@ dependencies = [ "url", ] +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -222,6 +325,15 @@ dependencies = [ "syn", ] +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -234,6 +346,58 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "autotools" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef941527c41b0fc0dd48511a8154cd5fc7e29200a0ff8b7203c5d777dbc795cf" +dependencies = [ + "cc", +] + +[[package]] +name = "av-scenechange" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec", + "log", + "num-rational", + "num-traits", + "pastey", + "rayon", + "thiserror", + "v_frame", + "y4m", +] + +[[package]] +name = "av1-grain" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom 8.0.0", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7178fe5f7d460b13895ebb9dcb28a3a6216d2df2574a0806cb51b555d297f38" +dependencies = [ + "arrayvec", +] + [[package]] name = "aws-lc-rs" version = "1.17.0" @@ -421,6 +585,24 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bentoml" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c65f4e07f4eac57a9e81aa97cf1c9d297cc01aad185767b613c1bcf112c469d8" +dependencies = [ + "bytes", + "jiff", + "reqwest", + "reqwest-middleware", + "reqwest-retry", + "serde", + "serde_json", + "thiserror", + "tracing", + "url", +] + [[package]] name = "bigdecimal" version = "0.4.10" @@ -450,6 +632,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + [[package]] name = "bitflags" version = "1.3.2" @@ -462,6 +650,15 @@ version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +[[package]] +name = "bitstream-io" +version = "4.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" +dependencies = [ + "no_std_io2", +] + [[package]] name = "blake2" version = "0.11.0-rc.6" @@ -510,18 +707,46 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "sha2 0.10.9", + "tinyvec", +] + +[[package]] +name = "built" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9" + [[package]] name = "bumpalo" version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.12.0" @@ -549,6 +774,16 @@ dependencies = [ "shlex", ] +[[package]] +name = "celes" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55028d5b1eebb35237512a3838ce5583211434a233c8bb179551a7197ffb7bd4" +dependencies = [ + "phf", + "serde", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -640,7 +875,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.1", ] [[package]] @@ -685,6 +920,21 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.5" @@ -742,6 +992,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cookie" version = "0.18.1" @@ -768,6 +1027,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "counter" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66e8e052be91f1c8aae2c1d81307d9f6e67f5f37001e3ddee419e971e73f03bc" +dependencies = [ + "num-traits", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -805,6 +1073,43 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.7" @@ -825,6 +1130,50 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "cssparser" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9cdaae01d5ed7882b04d795e7f752f46ff52d2fa3b50a20d28c464510bba98" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a2a99df6e410a8ff4245aa2006499ea662245f967cc7c0a38c83ef8eb44dbf" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "csv" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "csv-core" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +dependencies = [ + "memchr", +] + [[package]] name = "ctutils" version = "0.4.2" @@ -900,7 +1249,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.11.1", "syn", ] @@ -914,7 +1263,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.11.1", "syn", ] @@ -927,7 +1276,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.11.1", "syn", ] @@ -964,6 +1313,20 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "6.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" version = "2.11.0" @@ -1023,19 +1386,60 @@ dependencies = [ ] [[package]] -name = "der" -version = "0.7.10" +name = "deluxe" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +checksum = "8ed332aaf752b459088acf3dd4eca323e3ef4b83c70a84ca48fb0ec5305f1488" dependencies = [ - "const-oid 0.9.6", - "pem-rfc7468", - "zeroize", + "deluxe-core", + "deluxe-macros", + "once_cell", + "proc-macro2", + "syn", ] [[package]] -name = "deranged" -version = "0.5.8" +name = "deluxe-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddada51c8576df9d6a8450c351ff63042b092c9458b8ac7d20f89cbd0ffd313" +dependencies = [ + "arrayvec", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn", +] + +[[package]] +name = "deluxe-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87546d9c837f0b7557e47b8bd6eae52c3c223141b76aa233c345c9ab41d9117" +dependencies = [ + "deluxe-core", + "heck 0.4.1", + "if_chain", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid 0.9.6", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ @@ -1089,7 +1493,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case", + "convert_case 0.10.0", "proc-macro2", "quote", "rustc_version", @@ -1244,6 +1648,21 @@ dependencies = [ "syn", ] +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + [[package]] name = "dunce" version = "1.0.5" @@ -1278,12 +1697,232 @@ dependencies = [ "subtle", ] +[[package]] +name = "ego-tree" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b04dc5a38e4f151a79d9f2451ae6037fb6eaf5cba34771f44781f80e508498e3" + [[package]] name = "either" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +[[package]] +name = "elide" +version = "0.1.0" +source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +dependencies = [ + "async-trait", + "elide-codec", + "elide-context", + "elide-core", + "elide-detection", + "elide-lingua", + "elide-llm", + "elide-ner", + "elide-orchestration", + "elide-pattern", + "elide-redaction", +] + +[[package]] +name = "elide-bento" +version = "0.1.0" +source = "git+https://github.com/nvisycom/runtime?branch=main#a85d6feed24f58bd0e20e4cdbfd8912bc3e0797e" +dependencies = [ + "async-trait", + "base64", + "bentoml", + "elide-core", + "elide-ner", + "elide-ocr", + "hipstr", + "serde", + "thiserror", +] + +[[package]] +name = "elide-codec" +version = "0.1.0" +source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +dependencies = [ + "async-trait", + "bytes", + "csv", + "ego-tree", + "elide-core", + "hex", + "hound", + "image", + "imageproc", + "mp3lame-encoder", + "quick-xml 0.41.0", + "scraper", + "serde_json", + "sha2 0.11.0", + "symphonia", + "tracing", + "zip", +] + +[[package]] +name = "elide-context" +version = "0.1.0" +source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +dependencies = [ + "async-trait", + "elide-core", + "hipstr", + "unicode-segmentation", +] + +[[package]] +name = "elide-core" +version = "0.1.0" +source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +dependencies = [ + "async-trait", + "bytes", + "celes", + "derive_builder", + "hipstr", + "jiff", + "oxilangtag", + "schemars", + "serde", + "type-map", + "uuid", +] + +[[package]] +name = "elide-detection" +version = "0.1.0" +source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +dependencies = [ + "async-trait", + "elide-core", + "futures", + "schemars", + "serde", + "tracing", +] + +[[package]] +name = "elide-lingua" +version = "0.1.0" +source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +dependencies = [ + "async-trait", + "elide-core", + "lingua", + "tracing", +] + +[[package]] +name = "elide-llm" +version = "0.1.0" +source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +dependencies = [ + "async-trait", + "derive_builder", + "elide-core", + "minijinja", + "reqwest-middleware", + "reqwest-retry", + "reqwest-tracing", + "rig", + "schemars", + "serde", + "thiserror", + "tracing", + "unicode-normalization", +] + +[[package]] +name = "elide-ner" +version = "0.1.0" +source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +dependencies = [ + "async-trait", + "derive_builder", + "elide-core", + "hipstr", + "schemars", + "serde", + "uuid", +] + +[[package]] +name = "elide-ocr" +version = "0.1.0" +source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +dependencies = [ + "async-trait", + "elide-core", + "uuid", +] + +[[package]] +name = "elide-orchestration" +version = "0.1.0" +source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +dependencies = [ + "bytes", + "elide-codec", + "elide-core", + "elide-detection", + "elide-redaction", + "erased-serde", + "serde", +] + +[[package]] +name = "elide-pattern" +version = "0.1.0" +source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +dependencies = [ + "aho-corasick", + "async-trait", + "bs58", + "csv", + "derive_builder", + "derive_more", + "elide-context", + "elide-core", + "iban_validate", + "phonenumber", + "regex", + "serde", + "toml 1.1.2+spec-1.1.0", +] + +[[package]] +name = "elide-redaction" +version = "0.1.0" +source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +dependencies = [ + "async-trait", + "bytes", + "elide-core", + "hex", + "hipstr", + "sha2 0.11.0", + "tracing", + "uuid", +] + +[[package]] +name = "elide-stt" +version = "0.1.0" +source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +dependencies = [ + "async-trait", + "elide-core", + "uuid", +] + [[package]] name = "email_address" version = "0.2.9" @@ -1293,6 +1932,18 @@ dependencies = [ "serde", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -1302,6 +1953,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1329,6 +2000,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom 7.1.3", + "pin-project-lite", +] + [[package]] name = "expect-json" version = "1.11.0" @@ -1358,6 +2040,27 @@ dependencies = [ "syn", ] +[[package]] +name = "exr" +version = "1.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "extended" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365" + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -1372,7 +2075,7 @@ checksum = "e1e1dacd0d2082dfcf1351c4bdd566bbe89a2b263235a2b50058f1e130a47277" dependencies = [ "bit-set", "regex-automata", - "regex-syntax", + "regex-syntax 0.8.11", ] [[package]] @@ -1380,6 +2083,24 @@ name = "fastrand" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "fax" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] [[package]] name = "fiat-crypto" @@ -1401,6 +2122,7 @@ checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", + "zlib-rs", ] [[package]] @@ -1440,6 +2162,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "fst" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a" + [[package]] name = "futures" version = "0.3.32" @@ -1511,6 +2239,16 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +[[package]] +name = "futures-timer" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" +dependencies = [ + "gloo-timers", + "send_wrapper", +] + [[package]] name = "futures-util" version = "0.3.32" @@ -1553,6 +2291,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "getopts" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -1587,11 +2334,65 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", "wasip2", "wasip3", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "glam" +version = "0.30.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" + +[[package]] +name = "glam" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556f6b2ea90b8d15a74e0e7bb41671c9bdf38cd9f78c284d750b9ce58a2b5be7" + +[[package]] +name = "glam" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f70749695b063ecbf6b62949ccccde2e733ec3ecbbd71d467dca4e5c6c97cca0" + +[[package]] +name = "glam" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f22fb22f065b308be0d8724e3706c7fa3fc2a6c7d6899df4cad7860e7a75436" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gloo-timers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "482ce8a491a501da4cd806bd190275363d674f2845005c6ddbd5d3e1dd54495d" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", ] [[package]] @@ -1613,6 +2414,32 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.5" @@ -1662,6 +2489,20 @@ dependencies = [ "http", ] +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin 0.9.8", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -1714,6 +2555,22 @@ dependencies = [ "digest 0.11.3", ] +[[package]] +name = "hound" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" + +[[package]] +name = "html5ever" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a1761807faccc9a19e86944bbf40610014066306f96edcdedc2fb714bcb7b8" +dependencies = [ + "log", + "markup5ever", +] + [[package]] name = "http" version = "1.4.1" @@ -1864,6 +2721,15 @@ dependencies = [ "cc", ] +[[package]] +name = "iban_validate" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f695c602ee9bb55c4100c3e50559461ea3f49ca0d543cf770720af8165dc756" +dependencies = [ + "arrayvec", +] + [[package]] name = "icu_collections" version = "2.2.0" @@ -1979,6 +2845,90 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "if_chain" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb" + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imageproc" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7b27bc0867dc40df08deb53d6e96342db6e0702e7ae33ed09a4eba33e594b05" +dependencies = [ + "ab_glyph", + "approx", + "getrandom 0.4.2", + "image", + "itertools 0.14.0", + "nalgebra", + "num", + "rand 0.10.1", + "rand_distr", + "rayon", + "rustdct", +] + +[[package]] +name = "imgref" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89194689a993ab15268672e99e7b0e19da2da3268ac682e8f02d29d4d1434cd7" + +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indexmap" version = "2.14.0" @@ -1991,6 +2941,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + [[package]] name = "inout" version = "0.1.4" @@ -2000,6 +2959,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "inventory" version = "0.3.24" @@ -2201,12 +3171,28 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +[[package]] +name = "lebe" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" + [[package]] name = "libc" version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "libfuzzer-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2" +dependencies = [ + "arbitrary", + "cc", +] + [[package]] name = "libm" version = "0.2.16" @@ -2214,14 +3200,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] -name = "libredox" -version = "0.1.17" +name = "libredox" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +dependencies = [ + "libc", +] + +[[package]] +name = "lingua" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40d9129bb9fe42c95d1bd420d6891607eaff17df16ee15674aed2d05b0ec8f4" +dependencies = [ + "counter", + "dashmap", + "fastrand", + "fst", + "include_dir", + "itertools 0.14.0", + "lingua-english-language-model", + "maplit", + "rayon", + "regex", + "serde", + "serde-wasm-bindgen", + "strum 0.27.2", + "strum_macros 0.27.2", + "wasm-bindgen", +] + +[[package]] +name = "lingua-english-language-model" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +checksum = "97102de08b134a49f1cce05a1b6f5bf08ef21fe858074ae2b794e7892c43dd4b" dependencies = [ - "libc", + "include_dir", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -2262,12 +3286,47 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "lru-slab" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "markup5ever" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7122d987ec5f704ee56f6e5b41a7d93722e9aae27ae07cafa4036c4d3f9757de" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + [[package]] name = "matchers" version = "0.2.0" @@ -2283,6 +3342,26 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "md-5" version = "0.11.0" @@ -2299,6 +3378,12 @@ version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +[[package]] +name = "memo-map" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b" + [[package]] name = "migrations_internals" version = "2.3.0" @@ -2306,7 +3391,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36c791ecdf977c99f45f23280405d7723727470f6689a5e6dbf513ac547ae10d" dependencies = [ "serde", - "toml", + "toml 0.9.12+spec-1.1.0", ] [[package]] @@ -2336,6 +3421,22 @@ dependencies = [ "unicase", ] +[[package]] +name = "minijinja" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3d648e68cea56d9858d535ee28f9538404e2dd8cb08ed0bd05dca379477f39" +dependencies = [ + "memo-map", + "serde", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -2357,6 +3458,36 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "mp3lame-encoder" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3bb37607c4df224a0b7316897493fb49bd27312cb1b399fc4a43d2d7f2d3d8" +dependencies = [ + "mp3lame-sys", +] + +[[package]] +name = "mp3lame-sys" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e3b1772db47828840702e5a2e05694527f731abadf9b931355d54035f019d8" +dependencies = [ + "autotools", + "cc", + "libc", +] + [[package]] name = "multer" version = "3.1.0" @@ -2374,6 +3505,40 @@ dependencies = [ "version_check", ] +[[package]] +name = "nalgebra" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc43a60c217b0c6ff46e47f26911015ad8d2e5a8be1af668c67e370d99a4346" +dependencies = [ + "approx", + "glam 0.30.10", + "glam 0.31.1", + "glam 0.32.1", + "glam 0.33.2", + "matrixmultiply", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nanoid" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" +dependencies = [ + "rand 0.8.6", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nix" version = "0.31.3" @@ -2401,6 +3566,40 @@ dependencies = [ "signatory", ] +[[package]] +name = "no_std_io2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" +dependencies = [ + "memchr", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -2458,6 +3657,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -2496,6 +3706,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2539,6 +3750,41 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "nvisy-core" +version = "0.1.0" +source = "git+https://github.com/nvisycom/runtime?branch=main#a85d6feed24f58bd0e20e4cdbfd8912bc3e0797e" +dependencies = [ + "derive_builder", + "derive_more", + "elide-llm", + "schemars", + "serde", + "serde_json", + "strum 0.28.0", + "thiserror", +] + +[[package]] +name = "nvisy-engine" +version = "0.1.0" +source = "git+https://github.com/nvisycom/runtime?branch=main#a85d6feed24f58bd0e20e4cdbfd8912bc3e0797e" +dependencies = [ + "bytes", + "elide", + "elide-bento", + "elide-core", + "elide-llm", + "elide-ocr", + "elide-stt", + "nvisy-core", + "nvisy-schema", + "schemars", + "serde", + "tracing", + "uuid", +] + [[package]] name = "nvisy-nats" version = "0.1.0" @@ -2601,13 +3847,32 @@ dependencies = [ "schemars", "serde", "serde_json", - "strum", + "strum 0.28.0", "thiserror", "tokio", "tracing", "uuid", ] +[[package]] +name = "nvisy-schema" +version = "0.1.0" +source = "git+https://github.com/nvisycom/runtime?branch=main#a85d6feed24f58bd0e20e4cdbfd8912bc3e0797e" +dependencies = [ + "bytes", + "derive_builder", + "derive_more", + "elide-core", + "hipstr", + "jiff", + "schemars", + "semver", + "serde", + "serde_json", + "strum 0.28.0", + "uuid", +] + [[package]] name = "nvisy-server" version = "0.1.0" @@ -2631,8 +3896,10 @@ dependencies = [ "jiff", "jsonwebtoken", "nvisy-base", + "nvisy-engine", "nvisy-nats", "nvisy-postgres", + "nvisy-schema", "nvisy-webhook", "rand 0.10.1", "regex", @@ -2640,7 +3907,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.11.0", - "strum", + "strum 0.28.0", "tempfile", "thiserror", "tokio", @@ -2675,7 +3942,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.11.0", - "strum", + "strum 0.28.0", "thiserror", "tokio", "tracing", @@ -2727,7 +3994,7 @@ dependencies = [ "nix", "parking_lot", "percent-encoding", - "quick-xml", + "quick-xml 0.40.1", "rand 0.10.1", "reqwest", "rustls-pki-types", @@ -2756,6 +4023,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "oncemutex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d11de466f4a3006fe8a5e7ec84e93b79c70cb992ae0aa0eb631ad2df8abfe2" + [[package]] name = "opaque-debug" version = "0.3.1" @@ -2768,6 +4041,33 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "ordered-float" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" +dependencies = [ + "num-traits", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "oxilangtag" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d3b4eb570abd4a1dcb062c31fd37b832264d9dc7292c3e69acfe926c87b063f" +dependencies = [ + "serde", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -2802,6 +4102,18 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + [[package]] name = "pem" version = "3.0.6" @@ -2863,10 +4175,44 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ + "phf_macros", "phf_shared", "serde", ] +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "phf_shared" version = "0.13.1" @@ -2876,6 +4222,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phonenumber" +version = "0.3.10+9.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d85d3cc5477bfe2f357939e7f02a5a566632508633af1a2c37577d0bef87d9" +dependencies = [ + "either", + "fnv", + "nom 7.1.3", + "once_cell", + "postcard", + "quick-xml 0.40.1", + "regex", + "regex-cache", + "serde", + "serde_derive", + "strum 0.27.2", + "thiserror", +] + [[package]] name = "pin-project" version = "1.1.13" @@ -2924,6 +4290,19 @@ version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.13.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -2950,6 +4329,19 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "heapless", + "serde", +] + [[package]] name = "postgres-protocol" version = "0.6.12" @@ -3014,6 +4406,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "pretty_assertions" version = "1.4.1" @@ -3034,6 +4432,34 @@ dependencies = [ "syn", ] +[[package]] +name = "primal-check" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" +dependencies = [ + "num-integer", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.12+spec-1.1.0", +] + [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -3065,6 +4491,46 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "pxfm" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-xml" version = "0.40.1" @@ -3075,6 +4541,15 @@ dependencies = [ "serde", ] +[[package]] +name = "quick-xml" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +dependencies = [ + "memchr", +] + [[package]] name = "quinn" version = "0.11.9" @@ -3229,6 +4704,92 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "rand_distr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d431c2703ccf129de4d45253c03f49ebb22b97d6ad79ee3ecfc7e3f4862c1d8" +dependencies = [ + "num-traits", + "rand 0.10.1", +] + +[[package]] +name = "rav1e" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" +dependencies = [ + "aligned-vec", + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av-scenechange", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.14.0", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "paste", + "profiling", + "rand 0.9.4", + "rand_chacha 0.9.0", + "simd_helpers", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -3267,7 +4828,7 @@ dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax", + "regex-syntax 0.8.11", ] [[package]] @@ -3278,9 +4839,33 @@ checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.11", +] + +[[package]] +name = "regex-cache" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f7b62d69743b8b94f353b6b7c3deb4c5582828328bcb8d5fedf214373808793" +dependencies = [ + "lru-cache", + "oncemutex", + "regex", + "regex-syntax 0.6.29", ] +[[package]] +name = "regex-lite" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.11" @@ -3295,6 +4880,7 @@ checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ "base64", "bytes", + "encoding_rs", "futures-core", "futures-util", "h2", @@ -3306,6 +4892,7 @@ dependencies = [ "hyper-util", "js-sys", "log", + "mime", "mime_guess", "percent-encoding", "pin-project-lite", @@ -3399,6 +4986,73 @@ dependencies = [ "rand 0.10.1", ] +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" + +[[package]] +name = "rig" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e98e2e8f01c4c5bc23f577983634fa4d5244ffb070ea14c23b1ea5bd406e5cac" +dependencies = [ + "rig-core", +] + +[[package]] +name = "rig-core" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80a4bc7a93b329c4e1a66d5fd211d79990e7331e3c701f057c29f135f548686d" +dependencies = [ + "as-any", + "async-stream", + "base64", + "bytes", + "eventsource-stream", + "fastrand", + "futures", + "futures-timer", + "glob", + "http", + "indexmap", + "mime", + "mime_guess", + "nanoid", + "ordered-float", + "pin-project-lite", + "reqwest", + "reqwest-middleware", + "rig-derive", + "schemars", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-tungstenite", + "tracing", + "tracing-futures", + "url", +] + +[[package]] +name = "rig-derive" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5531bfa887b371eab658a92de7db35003370bbeee208ff5e68bbb81a5ae92d3d" +dependencies = [ + "convert_case 0.11.0", + "deluxe", + "indoc", + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "serde_json", + "syn", +] + [[package]] name = "ring" version = "0.17.14" @@ -3443,6 +5097,29 @@ dependencies = [ "semver", ] +[[package]] +name = "rustdct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b61555105d6a9bf98797c063c362a1d24ed8ab0431655e38f1cf51e52089551" +dependencies = [ + "rustfft", +] + +[[package]] +name = "rustfft" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21db5f9893e91f41798c88680037dba611ca6674703c1a18601b01a72c8adb89" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "primal-check", + "strength_reduce", + "transpose", +] + [[package]] name = "rustix" version = "1.1.4" @@ -3544,6 +5221,15 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "safe_arch" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7caad094bd561859bcd467734a720c3c1f5d1f338995351fefe2190c45efed" +dependencies = [ + "bytemuck", +] + [[package]] name = "same-file" version = "1.0.6" @@ -3568,6 +5254,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ + "bytes", "dyn-clone", "indexmap", "jiff", @@ -3603,6 +5290,21 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scraper" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd0be4d296f048bfb06dd01bbc80ef789ddd2e55583e8d2e6b804942abfabc2" +dependencies = [ + "cssparser", + "ego-tree", + "getopts", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", +] + [[package]] name = "security-framework" version = "3.7.0" @@ -3626,11 +5328,40 @@ dependencies = [ "libc", ] +[[package]] +name = "selectors" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8adfa1c298912827b8a28b223b3b874357397ae706e6190acd9bf28cee99114d" +dependencies = [ + "bitflags 2.13.0", + "cssparser", + "derive_more", + "log", + "new_debug_unreachable", + "phf", + "phf_codegen", + "precomputed-hash", + "rustc-hash", + "servo_arc", + "smallvec", +] + [[package]] name = "semver" version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" @@ -3642,6 +5373,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -3764,6 +5506,15 @@ dependencies = [ "serde", ] +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "sha1" version = "0.10.6" @@ -3844,6 +5595,18 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simba" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f45c644a9f3a386f9288625d9f0c1e999e1acf07a37df35d0516c7f199d9cb2" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "wide", +] + [[package]] name = "simd-adler32" version = "0.3.9" @@ -3860,6 +5623,15 @@ dependencies = [ "simdutf8", ] +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -3911,6 +5683,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spin" @@ -3934,6 +5709,36 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", +] + [[package]] name = "stringprep" version = "0.1.5" @@ -3945,19 +5750,46 @@ dependencies = [ "unicode-properties", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros 0.27.2", +] + [[package]] name = "strum" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" dependencies = [ - "strum_macros", + "strum_macros 0.28.0", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -3978,6 +5810,80 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "symphonia" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1758d6c853020a7244de03cc3e0185eaea3f58715122422dd3cc7452e6d4c16a" +dependencies = [ + "lazy_static", + "symphonia-bundle-mp3", + "symphonia-codec-pcm", + "symphonia-core", + "symphonia-format-riff", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-bundle-mp3" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350f1f2f2e19ad4dd315db94304d1eb361b29af070681f94e51b8fdaad769546" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-pcm" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50baee168f0e9dcf6ba7fc06e8b57eb62072a4490cc7cf13af77e72baae5d328" +dependencies = [ + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-core" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ec293b5f288383b72a7bffcade6b2860b642cf66f28b3bd5967349a49938b1" +dependencies = [ + "bitflags 2.13.0", + "bytemuck", + "lazy_static", + "log", + "num-complex", + "smallvec", +] + +[[package]] +name = "symphonia-format-riff" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17424452a777666d3eaf09a5c651029b15b6a333812fcc5b5474f2a3f0cff3f0" +dependencies = [ + "extended", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-metadata" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31acf5cd623398a6208e2225d18f4b20f761c55098a796a5247ad516a4a8681" +dependencies = [ + "lazy_static", + "log", + "regex-lite", + "smallvec", + "symphonia-core", +] + [[package]] name = "syn" version = "2.0.117" @@ -4022,6 +5928,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tendril" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" +dependencies = [ + "new_debug_unreachable", + "utf-8", +] + [[package]] name = "thiserror" version = "2.0.18" @@ -4051,6 +5967,20 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + [[package]] name = "time" version = "0.3.47" @@ -4181,6 +6111,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots 0.26.11", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -4223,11 +6169,32 @@ checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "serde_core", "serde_spanned", - "toml_datetime", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "winnow 0.7.15", ] +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.3", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" @@ -4237,6 +6204,38 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + [[package]] name = "toml_parser" version = "1.1.2+spec-1.1.0" @@ -4246,6 +6245,12 @@ dependencies = [ "winnow 1.0.3", ] +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + [[package]] name = "tower" version = "0.5.3" @@ -4342,6 +6347,18 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "futures", + "futures-task", + "pin-project", + "tracing", +] + [[package]] name = "tracing-log" version = "0.2.0" @@ -4384,6 +6401,16 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -4400,6 +6427,46 @@ dependencies = [ "tokio", ] +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.4", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + +[[package]] +name = "type-map" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" +dependencies = [ + "rustc-hash", +] + +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + [[package]] name = "typeid" version = "1.0.3" @@ -4475,6 +6542,12 @@ version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -4516,6 +6589,12 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -4540,6 +6619,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "v_frame" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "validator" version = "0.20.0" @@ -4785,6 +6875,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "075474b12bcb3d2e3d4546580e9de478eeeead668a1761e2a8860c836b7ef297" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + [[package]] name = "webpki-root-certs" version = "1.0.7" @@ -4812,6 +6914,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + [[package]] name = "whoami" version = "2.1.2" @@ -4825,6 +6933,16 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wide" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfdfe6a32973f2d1b268b8895845a8a96cac2f0191e72c27cc929036060dbf89" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "winapi-util" version = "0.1.11" @@ -5049,6 +7167,15 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.7.15" @@ -5060,6 +7187,9 @@ name = "winnow" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] [[package]] name = "wit-bindgen" @@ -5171,6 +7301,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + [[package]] name = "yansi" version = "1.0.1" @@ -5294,12 +7430,44 @@ dependencies = [ "syn", ] +[[package]] +name = "zip" +version = "8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" +dependencies = [ + "crc32fast", + "flate2", + "indexmap", + "memchr", + "typed-path", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5431d5661c32445236631278f27946e444ddafe4684cac70b185272d4f9c52d5" + [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] + [[package]] name = "zstd" version = "0.13.3" @@ -5328,6 +7496,30 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] + [[package]] name = "zxcvbn" version = "3.1.1" diff --git a/Cargo.toml b/Cargo.toml index 0af3cc5..6d820d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,12 @@ documentation = "https://docs.rs/nvisy-server" # # See for more details: https://github.com/rust-lang/cargo/issues/11329 +# Runtime crates (detection & redaction engine); tracks main. +# The engine needs its default modality features (tabular/image/audio/ +# document); disabling them selects an uncompilable feature combination. +nvisy-engine = { git = "https://github.com/nvisycom/runtime", branch = "main" } +nvisy-schema = { git = "https://github.com/nvisycom/runtime", branch = "main", default-features = false } + # Internal crates nvisy-base = { path = "./crates/nvisy-base", version = "0.1.0" } nvisy-nats = { path = "./crates/nvisy-nats", version = "0.1.0" } diff --git a/crates/nvisy-server/Cargo.toml b/crates/nvisy-server/Cargo.toml index 6fa699b..5e0bd50 100644 --- a/crates/nvisy-server/Cargo.toml +++ b/crates/nvisy-server/Cargo.toml @@ -27,6 +27,10 @@ rustdoc-args = ["--cfg", "docsrs"] default = [] [dependencies] +# Runtime crates +nvisy-engine = { workspace = true } +nvisy-schema = { workspace = true } + # Internal crates nvisy-base = { workspace = true, features = ["schema"] } nvisy-nats = { workspace = true, features = ["schema"] } From 502f232159bcbc4179a2127a6feea18c6df7ae20 Mon Sep 17 00:00:00 2001 From: Oleh Martsokha Date: Fri, 3 Jul 2026 05:34:25 +0200 Subject: [PATCH 2/7] feat(postgres): restructure contexts + add policies tables Replace the context-file model (encrypted NATS blob metadata: storage_key/mime_type/content_hash) with the structured engine Context: a `version` + `definition` JSONB holding a nvisy_schema Context. Add a parallel workspace_policies table for nvisy_schema Policy config. Both are the config the redaction engine consumes. Regenerates schema.rs. The Rust model/query/handler layers are updated in the following commits. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/nvisy-postgres/src/schema.rs | 46 +++++--- .../2026-01-19-045012_pipelines/down.sql | 3 + migrations/2026-01-19-045012_pipelines/up.sql | 104 ++++++++++++++---- 3 files changed, 120 insertions(+), 33 deletions(-) diff --git a/crates/nvisy-postgres/src/schema.rs b/crates/nvisy-postgres/src/schema.rs index 2ab2897..b1a18cc 100644 --- a/crates/nvisy-postgres/src/schema.rs +++ b/crates/nvisy-postgres/src/schema.rs @@ -166,17 +166,18 @@ diesel::table! { diesel::table! { use diesel::sql_types::*; use pgvector::sql_types::*; + use super::sql_types::SyncStatus; - workspace_contexts (id) { + workspace_connections (id) { id -> Uuid, workspace_id -> Uuid, account_id -> Uuid, name -> Text, - description -> Nullable, - mime_type -> Text, - storage_key -> Text, - content_size -> Int8, - content_hash -> Bytea, + provider -> Text, + encrypted_data -> Bytea, + is_active -> Bool, + last_sync_at -> Nullable, + sync_status -> Nullable, metadata -> Jsonb, created_at -> Timestamptz, updated_at -> Timestamptz, @@ -187,18 +188,15 @@ diesel::table! { diesel::table! { use diesel::sql_types::*; use pgvector::sql_types::*; - use super::sql_types::SyncStatus; - workspace_connections (id) { + workspace_contexts (id) { id -> Uuid, workspace_id -> Uuid, account_id -> Uuid, name -> Text, - provider -> Text, - encrypted_data -> Bytea, - is_active -> Bool, - last_sync_at -> Nullable, - sync_status -> Nullable, + description -> Nullable, + version -> Text, + definition -> Jsonb, metadata -> Jsonb, created_at -> Timestamptz, updated_at -> Timestamptz, @@ -371,6 +369,25 @@ diesel::table! { } } +diesel::table! { + use diesel::sql_types::*; + use pgvector::sql_types::*; + + workspace_policies (id) { + id -> Uuid, + workspace_id -> Uuid, + account_id -> Uuid, + name -> Text, + description -> Nullable, + version -> Text, + definition -> Jsonb, + metadata -> Jsonb, + created_at -> Timestamptz, + updated_at -> Timestamptz, + deleted_at -> Nullable, + } +} + diesel::table! { use diesel::sql_types::*; use pgvector::sql_types::*; @@ -438,6 +455,8 @@ diesel::joinable!(workspace_pipeline_runs -> accounts (account_id)); diesel::joinable!(workspace_pipeline_runs -> workspace_pipelines (pipeline_id)); diesel::joinable!(workspace_pipelines -> accounts (account_id)); diesel::joinable!(workspace_pipelines -> workspaces (workspace_id)); +diesel::joinable!(workspace_policies -> accounts (account_id)); +diesel::joinable!(workspace_policies -> workspaces (workspace_id)); diesel::joinable!(workspace_webhooks -> accounts (created_by)); diesel::joinable!(workspace_webhooks -> workspaces (workspace_id)); diesel::joinable!(workspaces -> accounts (created_by)); @@ -458,6 +477,7 @@ diesel::allow_tables_to_appear_in_same_query!( workspace_pipeline_artifacts, workspace_pipeline_runs, workspace_pipelines, + workspace_policies, workspace_webhooks, workspaces, ); diff --git a/migrations/2026-01-19-045012_pipelines/down.sql b/migrations/2026-01-19-045012_pipelines/down.sql index 6f6f6f5..a73e2c1 100644 --- a/migrations/2026-01-19-045012_pipelines/down.sql +++ b/migrations/2026-01-19-045012_pipelines/down.sql @@ -21,6 +21,9 @@ DROP TYPE IF EXISTS PIPELINE_STATUS; -- Workspace contexts DROP TABLE IF EXISTS workspace_contexts; +-- Workspace policies +DROP TABLE IF EXISTS workspace_policies; + -- Workspace connections DROP TABLE IF EXISTS workspace_connections; diff --git a/migrations/2026-01-19-045012_pipelines/up.sql b/migrations/2026-01-19-045012_pipelines/up.sql index 84729bf..4424939 100644 --- a/migrations/2026-01-19-045012_pipelines/up.sql +++ b/migrations/2026-01-19-045012_pipelines/up.sql @@ -91,7 +91,79 @@ COMMENT ON COLUMN workspace_connections.created_at IS 'Creation timestamp'; COMMENT ON COLUMN workspace_connections.updated_at IS 'Last modification timestamp'; COMMENT ON COLUMN workspace_connections.deleted_at IS 'Soft deletion timestamp'; --- Workspace context files table (metadata for encrypted context stored in NATS) +-- Workspace redaction policies table (structured governance config) +-- The definition holds a nvisy_schema::policy::Policy the engine consumes. +CREATE TABLE workspace_policies ( + -- Primary identifier + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + -- References + workspace_id UUID NOT NULL REFERENCES workspaces (id) ON DELETE CASCADE, + account_id UUID NOT NULL REFERENCES accounts (id) ON DELETE CASCADE, + + -- Core attributes + name TEXT NOT NULL, + description TEXT DEFAULT NULL, + version TEXT NOT NULL, + + CONSTRAINT workspace_policies_name_length CHECK (length(trim(name)) BETWEEN 1 AND 255), + CONSTRAINT workspace_policies_description_length CHECK (description IS NULL OR length(description) <= 4096), + CONSTRAINT workspace_policies_version_length CHECK (length(trim(version)) BETWEEN 1 AND 64), + + -- Policy body (nvisy_schema::policy::Policy as JSON: rules, labels, + -- fallback, retention, applies_when predicate). + definition JSONB NOT NULL, + + CONSTRAINT workspace_policies_definition_size CHECK (length(definition::TEXT) BETWEEN 2 AND 1048576), + + -- Metadata (for filtering/display) + metadata JSONB NOT NULL DEFAULT '{}', + + CONSTRAINT workspace_policies_metadata_size CHECK (length(metadata::TEXT) BETWEEN 2 AND 65536), + + -- Lifecycle timestamps + created_at TIMESTAMPTZ NOT NULL DEFAULT current_timestamp, + updated_at TIMESTAMPTZ NOT NULL DEFAULT current_timestamp, + deleted_at TIMESTAMPTZ DEFAULT NULL, + + CONSTRAINT workspace_policies_updated_after_created CHECK (updated_at >= created_at), + CONSTRAINT workspace_policies_deleted_after_created CHECK (deleted_at IS NULL OR deleted_at >= created_at) +); + +-- Triggers +SELECT setup_updated_at('workspace_policies'); + +-- Indexes +CREATE INDEX workspace_policies_workspace_idx + ON workspace_policies (workspace_id, created_at DESC) + WHERE deleted_at IS NULL; + +CREATE INDEX workspace_policies_account_idx + ON workspace_policies (account_id, created_at DESC) + WHERE deleted_at IS NULL; + +CREATE UNIQUE INDEX workspace_policies_name_unique_idx + ON workspace_policies (workspace_id, lower(trim(name))) + WHERE deleted_at IS NULL; + +-- Comments +COMMENT ON TABLE workspace_policies IS + 'Structured redaction policies (nvisy_schema Policy) consumed by the engine.'; + +COMMENT ON COLUMN workspace_policies.id IS 'Unique policy identifier'; +COMMENT ON COLUMN workspace_policies.workspace_id IS 'Parent workspace reference'; +COMMENT ON COLUMN workspace_policies.account_id IS 'Creator account reference'; +COMMENT ON COLUMN workspace_policies.name IS 'Human-readable policy name (1-255 chars)'; +COMMENT ON COLUMN workspace_policies.description IS 'Policy description (up to 4096 chars)'; +COMMENT ON COLUMN workspace_policies.version IS 'Semver of the policy body'; +COMMENT ON COLUMN workspace_policies.definition IS 'Policy body as JSON (rules, labels, fallback, retention)'; +COMMENT ON COLUMN workspace_policies.metadata IS 'Metadata for filtering/display'; +COMMENT ON COLUMN workspace_policies.created_at IS 'Creation timestamp'; +COMMENT ON COLUMN workspace_policies.updated_at IS 'Last modification timestamp'; +COMMENT ON COLUMN workspace_policies.deleted_at IS 'Soft deletion timestamp'; + +-- Workspace contexts table (structured reference-data for redaction) +-- The definition holds a nvisy_schema::context::Context the engine consumes. CREATE TABLE workspace_contexts ( -- Primary identifier id UUID PRIMARY KEY DEFAULT gen_random_uuid(), @@ -103,25 +175,19 @@ CREATE TABLE workspace_contexts ( -- Core attributes name TEXT NOT NULL, description TEXT DEFAULT NULL, - mime_type TEXT NOT NULL DEFAULT 'application/json', + version TEXT NOT NULL, CONSTRAINT workspace_contexts_name_length CHECK (length(trim(name)) BETWEEN 1 AND 255), CONSTRAINT workspace_contexts_description_length CHECK (description IS NULL OR length(description) <= 4096), - CONSTRAINT workspace_contexts_mime_type_length CHECK (length(trim(mime_type)) BETWEEN 1 AND 128), - - -- Storage reference (NATS object store key) - storage_key TEXT NOT NULL, + CONSTRAINT workspace_contexts_version_length CHECK (length(trim(version)) BETWEEN 1 AND 64), - CONSTRAINT workspace_contexts_storage_key_length CHECK (length(trim(storage_key)) BETWEEN 1 AND 512), + -- Context body (nvisy_schema::context::Context as JSON: typed + -- reference-data entries — biometric, geospatial, temporal, ...). + definition JSONB NOT NULL, - -- Content metadata - content_size BIGINT NOT NULL, - content_hash BYTEA NOT NULL, + CONSTRAINT workspace_contexts_definition_size CHECK (length(definition::TEXT) BETWEEN 2 AND 1048576), - CONSTRAINT workspace_contexts_content_size_positive CHECK (content_size > 0), - CONSTRAINT workspace_contexts_content_hash_length CHECK (length(content_hash) = 32), - - -- Metadata (non-encrypted, for filtering/display) + -- Metadata (for filtering/display) metadata JSONB NOT NULL DEFAULT '{}', CONSTRAINT workspace_contexts_metadata_size CHECK (length(metadata::TEXT) BETWEEN 2 AND 65536), @@ -153,18 +219,16 @@ CREATE UNIQUE INDEX workspace_contexts_name_unique_idx -- Comments COMMENT ON TABLE workspace_contexts IS - 'Metadata for encrypted context files stored in NATS object storage.'; + 'Structured reference-data contexts (nvisy_schema Context) consumed by the engine.'; COMMENT ON COLUMN workspace_contexts.id IS 'Unique context identifier'; COMMENT ON COLUMN workspace_contexts.workspace_id IS 'Parent workspace reference'; COMMENT ON COLUMN workspace_contexts.account_id IS 'Creator account reference'; COMMENT ON COLUMN workspace_contexts.name IS 'Human-readable context name (1-255 chars)'; COMMENT ON COLUMN workspace_contexts.description IS 'Context description (up to 4096 chars)'; -COMMENT ON COLUMN workspace_contexts.mime_type IS 'Content MIME type'; -COMMENT ON COLUMN workspace_contexts.storage_key IS 'NATS object store key for the encrypted content'; -COMMENT ON COLUMN workspace_contexts.content_size IS 'Size of the encrypted content in bytes'; -COMMENT ON COLUMN workspace_contexts.content_hash IS 'SHA-256 hash of the encrypted content'; -COMMENT ON COLUMN workspace_contexts.metadata IS 'Non-encrypted metadata for filtering/display'; +COMMENT ON COLUMN workspace_contexts.version IS 'Semver of the context body'; +COMMENT ON COLUMN workspace_contexts.definition IS 'Context body as JSON (typed reference-data entries)'; +COMMENT ON COLUMN workspace_contexts.metadata IS 'Metadata for filtering/display'; COMMENT ON COLUMN workspace_contexts.created_at IS 'Creation timestamp'; COMMENT ON COLUMN workspace_contexts.updated_at IS 'Last modification timestamp'; COMMENT ON COLUMN workspace_contexts.deleted_at IS 'Soft deletion timestamp'; From 437e667f079cbd32a769524d7b6cac1be09e2cbc Mon Sep 17 00:00:00 2001 From: Oleh Martsokha Date: Fri, 3 Jul 2026 05:38:33 +0200 Subject: [PATCH 3/7] feat(postgres): structured context + policy models and queries Rewrite WorkspaceContext to the structured shape (version + definition JSONB, dropping storage_key/mime_type/content_hash) and add the parallel WorkspacePolicy model + repository. The context repository was already column-agnostic, so only the model changed there. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/nvisy-postgres/src/model/mod.rs | 2 + .../src/model/workspace_context.rs | 48 ++-- .../src/model/workspace_policy.rs | 106 ++++++++ crates/nvisy-postgres/src/query/mod.rs | 2 + .../src/query/workspace_policy.rs | 248 ++++++++++++++++++ 5 files changed, 376 insertions(+), 30 deletions(-) create mode 100644 crates/nvisy-postgres/src/model/workspace_policy.rs create mode 100644 crates/nvisy-postgres/src/query/workspace_policy.rs diff --git a/crates/nvisy-postgres/src/model/mod.rs b/crates/nvisy-postgres/src/model/mod.rs index 340c3bf..e97ecb8 100644 --- a/crates/nvisy-postgres/src/model/mod.rs +++ b/crates/nvisy-postgres/src/model/mod.rs @@ -19,6 +19,7 @@ mod workspace_member; mod workspace_pipeline; mod workspace_pipeline_artifact; mod workspace_pipeline_run; +mod workspace_policy; mod workspace_webhook; // Account models @@ -53,4 +54,5 @@ pub use workspace_pipeline_artifact::{NewWorkspacePipelineArtifact, WorkspacePip pub use workspace_pipeline_run::{ NewWorkspacePipelineRun, UpdateWorkspacePipelineRun, WorkspacePipelineRun, }; +pub use workspace_policy::{NewWorkspacePolicy, UpdateWorkspacePolicy, WorkspacePolicy}; pub use workspace_webhook::{NewWorkspaceWebhook, UpdateWorkspaceWebhook, WorkspaceWebhook}; diff --git a/crates/nvisy-postgres/src/model/workspace_context.rs b/crates/nvisy-postgres/src/model/workspace_context.rs index d7209c0..117efba 100644 --- a/crates/nvisy-postgres/src/model/workspace_context.rs +++ b/crates/nvisy-postgres/src/model/workspace_context.rs @@ -8,10 +8,10 @@ use uuid::Uuid; use crate::schema::workspace_contexts; use crate::types::{HasCreatedAt, HasDeletedAt, HasUpdatedAt}; -/// Workspace context model representing metadata for encrypted context files. +/// Workspace context representing structured reference-data for redaction. /// -/// The actual encrypted content is stored in NATS object storage. -/// This record holds the metadata and storage reference. +/// The `definition` holds a `nvisy_schema` Context (typed reference-data +/// entries) that the redaction engine consumes. #[derive(Debug, Clone, PartialEq, Queryable, Selectable)] #[diesel(table_name = workspace_contexts)] #[diesel(check_for_backend(diesel::pg::Pg))] @@ -26,15 +26,11 @@ pub struct WorkspaceContext { pub name: String, /// Context description. pub description: Option, - /// Content MIME type. - pub mime_type: String, - /// NATS object store key for the encrypted content. - pub storage_key: String, - /// Size of the encrypted content in bytes. - pub content_size: i64, - /// SHA-256 hash of the encrypted content. - pub content_hash: Vec, - /// Non-encrypted metadata for filtering/display. + /// Semver of the context body. + pub version: String, + /// Context body (the engine's Context type as JSON). + pub definition: JsonValue, + /// Metadata for filtering/display. pub metadata: JsonValue, /// Timestamp when the context was created. pub created_at: Timestamp, @@ -57,15 +53,11 @@ pub struct NewWorkspaceContext { pub name: String, /// Context description. pub description: Option, - /// Content MIME type. - pub mime_type: String, - /// NATS object store key. - pub storage_key: String, - /// Size of the encrypted content in bytes. - pub content_size: i64, - /// SHA-256 hash of the encrypted content. - pub content_hash: Vec, - /// Non-encrypted metadata for filtering/display. + /// Semver of the context body. + pub version: String, + /// Context body (the engine's Context type as JSON). + pub definition: JsonValue, + /// Metadata for filtering/display. pub metadata: Option, } @@ -78,15 +70,11 @@ pub struct UpdateWorkspaceContext { pub name: Option, /// Context description. pub description: Option>, - /// Content MIME type. - pub mime_type: Option, - /// NATS object store key (updated on content replacement). - pub storage_key: Option, - /// Size of the encrypted content in bytes. - pub content_size: Option, - /// SHA-256 hash of the encrypted content. - pub content_hash: Option>, - /// Non-encrypted metadata for filtering/display. + /// Semver of the context body. + pub version: Option, + /// Context body (the engine's Context type as JSON). + pub definition: Option, + /// Metadata for filtering/display. pub metadata: Option, /// Soft delete timestamp. pub deleted_at: Option>, diff --git a/crates/nvisy-postgres/src/model/workspace_policy.rs b/crates/nvisy-postgres/src/model/workspace_policy.rs new file mode 100644 index 0000000..2dc7e90 --- /dev/null +++ b/crates/nvisy-postgres/src/model/workspace_policy.rs @@ -0,0 +1,106 @@ +//! Workspace policy model for PostgreSQL database operations. + +use diesel::prelude::*; +use jiff_diesel::Timestamp; +use serde_json::Value as JsonValue; +use uuid::Uuid; + +use crate::schema::workspace_policies; +use crate::types::{HasCreatedAt, HasDeletedAt, HasUpdatedAt}; + +/// Workspace policy representing a structured redaction governance policy. +/// +/// The `definition` holds a `nvisy_schema` Policy (rules, labels, fallback, +/// retention) that the redaction engine consumes. +#[derive(Debug, Clone, PartialEq, Queryable, Selectable)] +#[diesel(table_name = workspace_policies)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct WorkspacePolicy { + /// Unique policy identifier. + pub id: Uuid, + /// Reference to the workspace this policy belongs to. + pub workspace_id: Uuid, + /// Reference to the account that created this policy. + pub account_id: Uuid, + /// Human-readable policy name. + pub name: String, + /// Policy description. + pub description: Option, + /// Semver of the policy body. + pub version: String, + /// Policy body (the engine's Policy type as JSON). + pub definition: JsonValue, + /// Metadata for filtering/display. + pub metadata: JsonValue, + /// Timestamp when the policy was created. + pub created_at: Timestamp, + /// Timestamp when the policy was last updated. + pub updated_at: Timestamp, + /// Timestamp when the policy was soft-deleted. + pub deleted_at: Option, +} + +/// Data for creating a new workspace policy. +#[derive(Debug, Clone, Insertable)] +#[diesel(table_name = workspace_policies)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct NewWorkspacePolicy { + /// Workspace ID (required). + pub workspace_id: Uuid, + /// Account ID (required). + pub account_id: Uuid, + /// Policy name. + pub name: String, + /// Policy description. + pub description: Option, + /// Semver of the policy body. + pub version: String, + /// Policy body (the engine's Policy type as JSON). + pub definition: JsonValue, + /// Metadata for filtering/display. + pub metadata: Option, +} + +/// Data for updating a workspace policy. +#[derive(Debug, Clone, Default, AsChangeset)] +#[diesel(table_name = workspace_policies)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct UpdateWorkspacePolicy { + /// Policy name. + pub name: Option, + /// Policy description. + pub description: Option>, + /// Semver of the policy body. + pub version: Option, + /// Policy body (the engine's Policy type as JSON). + pub definition: Option, + /// Metadata for filtering/display. + pub metadata: Option, + /// Soft delete timestamp. + pub deleted_at: Option>, +} + +impl WorkspacePolicy { + /// Returns whether the policy is deleted. + pub fn is_deleted(&self) -> bool { + self.deleted_at.is_some() + } +} + +impl HasCreatedAt for WorkspacePolicy { + fn created_at(&self) -> jiff::Timestamp { + self.created_at.into() + } +} + +impl HasUpdatedAt for WorkspacePolicy { + fn updated_at(&self) -> jiff::Timestamp { + self.updated_at.into() + } +} + +impl HasDeletedAt for WorkspacePolicy { + fn deleted_at(&self) -> Option { + self.deleted_at.map(Into::into) + } +} diff --git a/crates/nvisy-postgres/src/query/mod.rs b/crates/nvisy-postgres/src/query/mod.rs index 291327b..1653661 100644 --- a/crates/nvisy-postgres/src/query/mod.rs +++ b/crates/nvisy-postgres/src/query/mod.rs @@ -29,6 +29,7 @@ mod workspace_member; mod workspace_pipeline; mod workspace_pipeline_artifact; mod workspace_pipeline_run; +mod workspace_policy; mod workspace_webhook; pub use account::AccountRepository; @@ -47,4 +48,5 @@ pub use workspace_member::WorkspaceMemberRepository; pub use workspace_pipeline::WorkspacePipelineRepository; pub use workspace_pipeline_artifact::WorkspacePipelineArtifactRepository; pub use workspace_pipeline_run::WorkspacePipelineRunRepository; +pub use workspace_policy::WorkspacePolicyRepository; pub use workspace_webhook::WorkspaceWebhookRepository; diff --git a/crates/nvisy-postgres/src/query/workspace_policy.rs b/crates/nvisy-postgres/src/query/workspace_policy.rs new file mode 100644 index 0000000..72e08a0 --- /dev/null +++ b/crates/nvisy-postgres/src/query/workspace_policy.rs @@ -0,0 +1,248 @@ +//! Workspace policies repository for managing redaction policy config. + +use std::future::Future; + +use diesel::prelude::*; +use diesel_async::RunQueryDsl; +use uuid::Uuid; + +use crate::model::{NewWorkspacePolicy, UpdateWorkspacePolicy, WorkspacePolicy}; +use crate::types::{CursorPage, CursorPagination, OffsetPagination}; +use crate::{PgConnection, PgError, PgResult, schema}; + +/// Repository for workspace policy database operations. +pub trait WorkspacePolicyRepository { + /// Creates a new workspace policy record. + fn create_workspace_policy( + &mut self, + new_policy: NewWorkspacePolicy, + ) -> impl Future> + Send; + + /// Finds a policy by its unique identifier. + fn find_workspace_policy_by_id( + &mut self, + policy_id: Uuid, + ) -> impl Future>> + Send; + + /// Finds a policy by ID within a specific workspace. + fn find_policy_in_workspace( + &mut self, + workspace_id: Uuid, + policy_id: Uuid, + ) -> impl Future>> + Send; + + /// Lists all policies in a workspace with offset pagination. + fn offset_list_workspace_policies( + &mut self, + workspace_id: Uuid, + pagination: OffsetPagination, + ) -> impl Future>> + Send; + + /// Lists all policies in a workspace with cursor pagination. + fn cursor_list_workspace_policies( + &mut self, + workspace_id: Uuid, + pagination: CursorPagination, + ) -> impl Future>> + Send; + + /// Updates a policy with new data. + fn update_workspace_policy( + &mut self, + policy_id: Uuid, + updates: UpdateWorkspacePolicy, + ) -> impl Future> + Send; + + /// Soft deletes a policy by setting the deletion timestamp. + fn delete_workspace_policy( + &mut self, + policy_id: Uuid, + ) -> impl Future> + Send; + + /// Counts policies in a workspace. + fn count_workspace_policies( + &mut self, + workspace_id: Uuid, + ) -> impl Future> + Send; +} + +impl WorkspacePolicyRepository for PgConnection { + async fn create_workspace_policy( + &mut self, + new_policy: NewWorkspacePolicy, + ) -> PgResult { + use schema::workspace_policies; + + let policy = diesel::insert_into(workspace_policies::table) + .values(&new_policy) + .returning(WorkspacePolicy::as_returning()) + .get_result(self) + .await + .map_err(PgError::from)?; + + Ok(policy) + } + + async fn find_workspace_policy_by_id( + &mut self, + policy_id: Uuid, + ) -> PgResult> { + use schema::workspace_policies::{self, dsl}; + + let policy = workspace_policies::table + .filter(dsl::id.eq(policy_id)) + .filter(dsl::deleted_at.is_null()) + .select(WorkspacePolicy::as_select()) + .first(self) + .await + .optional() + .map_err(PgError::from)?; + + Ok(policy) + } + + async fn find_policy_in_workspace( + &mut self, + workspace_id: Uuid, + policy_id: Uuid, + ) -> PgResult> { + use schema::workspace_policies::{self, dsl}; + + let policy = workspace_policies::table + .filter(dsl::id.eq(policy_id)) + .filter(dsl::workspace_id.eq(workspace_id)) + .filter(dsl::deleted_at.is_null()) + .select(WorkspacePolicy::as_select()) + .first(self) + .await + .optional() + .map_err(PgError::from)?; + + Ok(policy) + } + + async fn offset_list_workspace_policies( + &mut self, + workspace_id: Uuid, + pagination: OffsetPagination, + ) -> PgResult> { + use schema::workspace_policies::{self, dsl}; + + let policies = workspace_policies::table + .filter(dsl::workspace_id.eq(workspace_id)) + .filter(dsl::deleted_at.is_null()) + .order(dsl::created_at.desc()) + .limit(pagination.limit) + .offset(pagination.offset) + .select(WorkspacePolicy::as_select()) + .load(self) + .await + .map_err(PgError::from)?; + + Ok(policies) + } + + async fn cursor_list_workspace_policies( + &mut self, + workspace_id: Uuid, + pagination: CursorPagination, + ) -> PgResult> { + use schema::workspace_policies::{self, dsl}; + + let total = if pagination.include_count { + Some( + workspace_policies::table + .filter(dsl::workspace_id.eq(workspace_id)) + .filter(dsl::deleted_at.is_null()) + .count() + .get_result::(self) + .await + .map_err(PgError::from)?, + ) + } else { + None + }; + + let query = workspace_policies::table + .filter(dsl::workspace_id.eq(workspace_id)) + .filter(dsl::deleted_at.is_null()) + .into_boxed(); + + let limit = pagination.limit + 1; + + let items: Vec = if let Some(cursor) = &pagination.after { + let cursor_time = jiff_diesel::Timestamp::from(cursor.timestamp); + + query + .filter( + dsl::created_at + .lt(&cursor_time) + .or(dsl::created_at.eq(&cursor_time).and(dsl::id.lt(cursor.id))), + ) + .select(WorkspacePolicy::as_select()) + .order((dsl::created_at.desc(), dsl::id.desc())) + .limit(limit) + .load(self) + .await + .map_err(PgError::from)? + } else { + query + .select(WorkspacePolicy::as_select()) + .order((dsl::created_at.desc(), dsl::id.desc())) + .limit(limit) + .load(self) + .await + .map_err(PgError::from)? + }; + + Ok(CursorPage::new( + items, + total, + pagination.limit, + |p: &WorkspacePolicy| (p.created_at.into(), p.id), + )) + } + + async fn update_workspace_policy( + &mut self, + policy_id: Uuid, + updates: UpdateWorkspacePolicy, + ) -> PgResult { + use schema::workspace_policies::{self, dsl}; + + let policy = diesel::update(workspace_policies::table.filter(dsl::id.eq(policy_id))) + .set(&updates) + .returning(WorkspacePolicy::as_returning()) + .get_result(self) + .await + .map_err(PgError::from)?; + + Ok(policy) + } + + async fn delete_workspace_policy(&mut self, policy_id: Uuid) -> PgResult<()> { + use diesel::dsl::now; + use schema::workspace_policies::{self, dsl}; + + diesel::update(workspace_policies::table.filter(dsl::id.eq(policy_id))) + .set(dsl::deleted_at.eq(now)) + .execute(self) + .await + .map_err(PgError::from)?; + + Ok(()) + } + + async fn count_workspace_policies(&mut self, workspace_id: Uuid) -> PgResult { + use schema::workspace_policies::{self, dsl}; + + let count = workspace_policies::table + .filter(dsl::workspace_id.eq(workspace_id)) + .filter(dsl::deleted_at.is_null()) + .count() + .get_result(self) + .await + .map_err(PgError::from)?; + + Ok(count) + } +} From ce3032f31ae975707f2148f562d00a641de430e7 Mon Sep 17 00:00:00 2001 From: Oleh Martsokha Date: Fri, 3 Jul 2026 07:55:19 +0200 Subject: [PATCH 4/7] feat(server): policy + context CRUD endpoints Replace the context-file uploader with structured, engine-backed context endpoints, and add parallel policy endpoints. Both carry a typed nvisy_schema definition (Context / Policy) validated at the API boundary and stored XChaCha20-Poly1305 encrypted (workspace-derived key) as BYTEA. - request/response types embed the schema types as typed OpenAPI fields - workspace-scoped CRUD: create, list, read, update, delete - new ViewPolicies / ManagePolicies permissions (Guest / Admin) - Page::try_from_cursor_page for fallible (decrypting) list mapping Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/model/workspace_context.rs | 12 +- .../src/model/workspace_policy.rs | 12 +- crates/nvisy-postgres/src/schema.rs | 4 +- .../src/extract/auth/permission.rs | 8 + crates/nvisy-server/src/handler/contexts.rs | 275 ++++----------- crates/nvisy-server/src/handler/mod.rs | 2 + crates/nvisy-server/src/handler/policies.rs | 312 ++++++++++++++++++ .../src/handler/request/contexts.rs | 21 ++ .../nvisy-server/src/handler/request/mod.rs | 2 + .../src/handler/request/policies.rs | 48 +++ .../src/handler/response/contexts.rs | 31 +- .../nvisy-server/src/handler/response/mod.rs | 17 + .../src/handler/response/policies.rs | 62 ++++ migrations/2026-01-19-045012_pipelines/up.sql | 16 +- 14 files changed, 571 insertions(+), 251 deletions(-) create mode 100644 crates/nvisy-server/src/handler/policies.rs create mode 100644 crates/nvisy-server/src/handler/request/policies.rs create mode 100644 crates/nvisy-server/src/handler/response/policies.rs diff --git a/crates/nvisy-postgres/src/model/workspace_context.rs b/crates/nvisy-postgres/src/model/workspace_context.rs index 117efba..ac4b8d6 100644 --- a/crates/nvisy-postgres/src/model/workspace_context.rs +++ b/crates/nvisy-postgres/src/model/workspace_context.rs @@ -28,8 +28,8 @@ pub struct WorkspaceContext { pub description: Option, /// Semver of the context body. pub version: String, - /// Context body (the engine's Context type as JSON). - pub definition: JsonValue, + /// Encrypted Context body (the engine's Context type as JSON). + pub definition: Vec, /// Metadata for filtering/display. pub metadata: JsonValue, /// Timestamp when the context was created. @@ -55,8 +55,8 @@ pub struct NewWorkspaceContext { pub description: Option, /// Semver of the context body. pub version: String, - /// Context body (the engine's Context type as JSON). - pub definition: JsonValue, + /// Encrypted Context body (the engine's Context type as JSON). + pub definition: Vec, /// Metadata for filtering/display. pub metadata: Option, } @@ -72,8 +72,8 @@ pub struct UpdateWorkspaceContext { pub description: Option>, /// Semver of the context body. pub version: Option, - /// Context body (the engine's Context type as JSON). - pub definition: Option, + /// Encrypted Context body (the engine's Context type as JSON). + pub definition: Option>, /// Metadata for filtering/display. pub metadata: Option, /// Soft delete timestamp. diff --git a/crates/nvisy-postgres/src/model/workspace_policy.rs b/crates/nvisy-postgres/src/model/workspace_policy.rs index 2dc7e90..0e3282c 100644 --- a/crates/nvisy-postgres/src/model/workspace_policy.rs +++ b/crates/nvisy-postgres/src/model/workspace_policy.rs @@ -28,8 +28,8 @@ pub struct WorkspacePolicy { pub description: Option, /// Semver of the policy body. pub version: String, - /// Policy body (the engine's Policy type as JSON). - pub definition: JsonValue, + /// Encrypted Policy body (the engine's Policy type as JSON). + pub definition: Vec, /// Metadata for filtering/display. pub metadata: JsonValue, /// Timestamp when the policy was created. @@ -55,8 +55,8 @@ pub struct NewWorkspacePolicy { pub description: Option, /// Semver of the policy body. pub version: String, - /// Policy body (the engine's Policy type as JSON). - pub definition: JsonValue, + /// Encrypted Policy body (the engine's Policy type as JSON). + pub definition: Vec, /// Metadata for filtering/display. pub metadata: Option, } @@ -72,8 +72,8 @@ pub struct UpdateWorkspacePolicy { pub description: Option>, /// Semver of the policy body. pub version: Option, - /// Policy body (the engine's Policy type as JSON). - pub definition: Option, + /// Encrypted Policy body (the engine's Policy type as JSON). + pub definition: Option>, /// Metadata for filtering/display. pub metadata: Option, /// Soft delete timestamp. diff --git a/crates/nvisy-postgres/src/schema.rs b/crates/nvisy-postgres/src/schema.rs index b1a18cc..027880d 100644 --- a/crates/nvisy-postgres/src/schema.rs +++ b/crates/nvisy-postgres/src/schema.rs @@ -196,7 +196,7 @@ diesel::table! { name -> Text, description -> Nullable, version -> Text, - definition -> Jsonb, + definition -> Bytea, metadata -> Jsonb, created_at -> Timestamptz, updated_at -> Timestamptz, @@ -380,7 +380,7 @@ diesel::table! { name -> Text, description -> Nullable, version -> Text, - definition -> Jsonb, + definition -> Bytea, metadata -> Jsonb, created_at -> Timestamptz, updated_at -> Timestamptz, diff --git a/crates/nvisy-server/src/extract/auth/permission.rs b/crates/nvisy-server/src/extract/auth/permission.rs index 4565143..165707f 100644 --- a/crates/nvisy-server/src/extract/auth/permission.rs +++ b/crates/nvisy-server/src/extract/auth/permission.rs @@ -71,6 +71,12 @@ pub enum Permission { /// Can create, modify, and manage workspace contexts. ManageContexts, + // Policy permissions + /// Can view workspace policies. + ViewPolicies, + /// Can create, modify, and manage workspace policies. + ManagePolicies, + // Webhook permissions /// Can view workspace webhooks. ViewWebhooks, @@ -105,6 +111,7 @@ impl Permission { | Self::ViewMembers | Self::ViewConnections | Self::ViewContexts + | Self::ViewPolicies | Self::ViewWebhooks => WorkspaceRole::Guest, // Member-level permissions (create and modify own resources) @@ -124,6 +131,7 @@ impl Permission { | Self::RemoveMembers | Self::ManageConnections | Self::ManageContexts + | Self::ManagePolicies | Self::CreateWebhooks | Self::UpdateWebhooks | Self::DeleteWebhooks diff --git a/crates/nvisy-server/src/handler/contexts.rs b/crates/nvisy-server/src/handler/contexts.rs index cfca9e9..5c014c1 100644 --- a/crates/nvisy-server/src/handler/contexts.rs +++ b/crates/nvisy-server/src/handler/contexts.rs @@ -1,38 +1,36 @@ -//! Workspace context file management handlers. +//! Workspace context management handlers. //! -//! Context files are encrypted JSON documents stored in NATS object storage. -//! The metadata (name, size, hash) is stored in PostgreSQL while the actual -//! content is encrypted with workspace-derived keys and stored as objects. - -use std::io::Cursor; +//! Contexts are structured reference-data documents (the engine's Context +//! type) consumed by the redaction pipeline. The definition is validated +//! against the schema, then stored encrypted (XChaCha20-Poly1305, workspace- +//! derived key) as a JSONB-free BYTEA column in PostgreSQL, scoped to a +//! workspace. use aide::axum::ApiRouter; use aide::transform::TransformOperation; -use axum::extract::{DefaultBodyLimit, State}; +use axum::extract::State; use axum::http::StatusCode; -use nvisy_nats::NatsClient; -use nvisy_nats::object::{ContextFilesBucket, ContextKey}; use nvisy_postgres::model::{NewWorkspaceContext, UpdateWorkspaceContext, WorkspaceContext}; use nvisy_postgres::query::WorkspaceContextRepository; use nvisy_postgres::{PgClient, PgConn}; use uuid::Uuid; -use crate::extract::{AuthProvider, AuthState, Json, Multipart, Path, Permission, Query}; -use crate::handler::request::{ContextPathParams, CursorPagination, WorkspacePathParams}; +use crate::extract::{AuthProvider, AuthState, Json, Path, Permission, Query, ValidateJson}; +use crate::handler::request::{ + ContextPathParams, CreateContext, CursorPagination, UpdateContext, WorkspacePathParams, +}; use crate::handler::response::{Context, ContextsPage, ErrorResponse}; -use crate::handler::{Error, ErrorKind, Result}; -use crate::middleware::DEFAULT_MAX_FILE_BODY_SIZE; +use crate::handler::{Error, Result}; use crate::service::{CryptoService, ServiceState}; /// Tracing target for workspace context operations. const TRACING_TARGET: &str = "nvisy_server::handler::contexts"; -/// Creates a new workspace context from a multipart upload. +/// Creates a new workspace context. /// -/// Expects a multipart form with: -/// - `name`: Context name (text field) -/// - `file`: The JSON context file (file field) -/// - `description`: Optional description (text field) +/// The request body carries a structured context definition; its name, +/// description, and version drive the stored record unless overridden. +/// Requires `ManageContexts` permission for the workspace. #[tracing::instrument( skip_all, fields( @@ -42,11 +40,10 @@ const TRACING_TARGET: &str = "nvisy_server::handler::contexts"; )] async fn create_context( State(pg_client): State, - State(nats_client): State, State(crypto): State, AuthState(auth_state): AuthState, Path(path_params): Path, - Multipart(mut multipart): Multipart, + ValidateJson(request): ValidateJson, ) -> Result<(StatusCode, Json)> { tracing::debug!(target: TRACING_TARGET, "Creating workspace context"); @@ -60,117 +57,37 @@ async fn create_context( ) .await?; - let mut name: Option = None; - let mut description: Option = None; - let mut file_content: Option> = None; - - while let Some(field) = multipart.next_field().await.map_err(|err| { - tracing::error!(target: TRACING_TARGET, error = %err, "Failed to read multipart field"); - ErrorKind::BadRequest - .with_message("Invalid multipart data") - .with_context(format!("Failed to parse multipart form: {}", err)) - })? { - let field_name = field.name().unwrap_or_default().to_string(); - - match field_name.as_str() { - "name" => { - name = Some(field.text().await.map_err(|err| { - ErrorKind::BadRequest - .with_message("Failed to read name field") - .with_context(err.to_string()) - })?); - } - "description" => { - description = Some(field.text().await.map_err(|err| { - ErrorKind::BadRequest - .with_message("Failed to read description field") - .with_context(err.to_string()) - })?); - } - "file" => { - let bytes = field.bytes().await.map_err(|err| { - ErrorKind::BadRequest - .with_message("Failed to read file content") - .with_context(err.to_string()) - })?; - - // Validate it's valid JSON - serde_json::from_slice::(&bytes).map_err(|err| { - ErrorKind::BadRequest - .with_message("Context file must be valid JSON") - .with_context(err.to_string()) - })?; - - file_content = Some(bytes.to_vec()); - } - _ => { - tracing::debug!( - target: TRACING_TARGET, - field = %field_name, - "Skipping unknown multipart field" - ); - } - } - } - - let name = - name.ok_or_else(|| ErrorKind::BadRequest.with_message("Missing required 'name' field"))?; - let content = file_content - .ok_or_else(|| ErrorKind::BadRequest.with_message("Missing required 'file' field"))?; - - // Encrypt the content with the workspace-derived key - let encrypted_content = crypto.encrypt(path_params.workspace_id, &content)?; - - // Generate the object store key - let context_id = Uuid::now_v7(); - let context_key = ContextKey::new(path_params.workspace_id, context_id); - - // Store encrypted content in NATS - let context_store = nats_client - .object_store::() - .await?; - - let reader = Cursor::new(encrypted_content); - let put_result = context_store.put(&context_key, reader).await?; + let definition = &request.definition; + let name = request.name.unwrap_or_else(|| definition.name.clone()); + let description = request + .description + .or_else(|| definition.description.clone()); + let version = definition.version.to_string(); + let encrypted = crypto.encrypt_json(path_params.workspace_id, definition)?; - tracing::debug!( - target: TRACING_TARGET, - context_key = %context_key, - size = put_result.size(), - "Context file stored in NATS" - ); - - // Create metadata record in PostgreSQL let new_context = NewWorkspaceContext { workspace_id: path_params.workspace_id, account_id: auth_state.account_id, name, description, - mime_type: "application/json".to_string(), - storage_key: context_key.to_string(), - content_size: put_result.size() as i64, - content_hash: put_result.sha256().to_vec(), + version, + definition: encrypted, metadata: None, }; let context = conn.create_workspace_context(new_context).await?; - tracing::info!( - target: TRACING_TARGET, - context_id = %context.id, - "Context created", - ); + tracing::info!(target: TRACING_TARGET, context_id = %context.id, "Context created"); - Ok((StatusCode::CREATED, Json(Context::from_model(context)))) + Ok(( + StatusCode::CREATED, + Json(Context::from_model(context, &crypto)?), + )) } fn create_context_docs(op: TransformOperation) -> TransformOperation { op.summary("Create context") - .description( - "Creates a new context file for the workspace via multipart upload. \ - The file content is encrypted and stored in NATS object storage. \ - Expects fields: 'name' (text), 'file' (JSON file), 'description' (optional text).", - ) + .description("Creates a structured reference-data context for the workspace.") .response::<201, Json>() .response::<400, Json>() .response::<401, Json>() @@ -187,6 +104,7 @@ fn create_context_docs(op: TransformOperation) -> TransformOperation { )] async fn list_contexts( State(pg_client): State, + State(crypto): State, AuthState(auth_state): AuthState, Path(path_params): Path, Query(pagination): Query, @@ -213,15 +131,15 @@ async fn list_contexts( "Workspace contexts listed", ); - Ok(( - StatusCode::OK, - Json(ContextsPage::from_cursor_page(page, Context::from_model)), - )) + let page = + ContextsPage::try_from_cursor_page(page, |model| Context::from_model(model, &crypto))?; + + Ok((StatusCode::OK, Json(page))) } fn list_contexts_docs(op: TransformOperation) -> TransformOperation { op.summary("List contexts") - .description("Returns all context files for the workspace with metadata.") + .description("Returns all contexts for the workspace.") .response::<200, Json>() .response::<401, Json>() .response::<403, Json>() @@ -237,6 +155,7 @@ fn list_contexts_docs(op: TransformOperation) -> TransformOperation { )] async fn read_context( State(pg_client): State, + State(crypto): State, AuthState(auth_state): AuthState, Path(path_params): Path, ) -> Result<(StatusCode, Json)> { @@ -252,12 +171,12 @@ async fn read_context( tracing::debug!(target: TRACING_TARGET, "Workspace context read"); - Ok((StatusCode::OK, Json(Context::from_model(context)))) + Ok((StatusCode::OK, Json(Context::from_model(context, &crypto)?))) } fn read_context_docs(op: TransformOperation) -> TransformOperation { op.summary("Get context") - .description("Returns context file metadata.") + .description("Returns a single context.") .response::<200, Json>() .response::<401, Json>() .response::<403, Json>() @@ -266,11 +185,8 @@ fn read_context_docs(op: TransformOperation) -> TransformOperation { /// Updates a workspace context. /// -/// Updates context metadata and optionally replaces the content via multipart. -/// Expects a multipart form with optional fields: -/// - `name`: New context name (text field) -/// - `description`: New description (text field) -/// - `file`: Replacement JSON context file (file field) +/// All fields are optional; replacing the definition replaces the whole +/// context body (and its version). Requires `ManageContexts` permission. #[tracing::instrument( skip_all, fields( @@ -280,11 +196,10 @@ fn read_context_docs(op: TransformOperation) -> TransformOperation { )] async fn update_context( State(pg_client): State, - State(nats_client): State, State(crypto): State, AuthState(auth_state): AuthState, Path(path_params): Path, - Multipart(mut multipart): Multipart, + ValidateJson(request): ValidateJson, ) -> Result<(StatusCode, Json)> { tracing::debug!(target: TRACING_TARGET, "Updating workspace context"); @@ -296,91 +211,34 @@ async fn update_context( .authorize_workspace(&mut conn, existing.workspace_id, Permission::ManageContexts) .await?; - let mut name: Option = None; - let mut description: Option> = None; - let mut file_content: Option> = None; - - while let Some(field) = multipart.next_field().await.map_err(|err| { - ErrorKind::BadRequest - .with_message("Invalid multipart data") - .with_context(format!("Failed to parse multipart form: {}", err)) - })? { - let field_name = field.name().unwrap_or_default().to_string(); - - match field_name.as_str() { - "name" => { - name = Some(field.text().await.map_err(|err| { - ErrorKind::BadRequest - .with_message("Failed to read name field") - .with_context(err.to_string()) - })?); - } - "description" => { - let text = field.text().await.map_err(|err| { - ErrorKind::BadRequest - .with_message("Failed to read description field") - .with_context(err.to_string()) - })?; - description = Some(if text.is_empty() { None } else { Some(text) }); - } - "file" => { - let bytes = field.bytes().await.map_err(|err| { - ErrorKind::BadRequest - .with_message("Failed to read file content") - .with_context(err.to_string()) - })?; - - serde_json::from_slice::(&bytes).map_err(|err| { - ErrorKind::BadRequest - .with_message("Context file must be valid JSON") - .with_context(err.to_string()) - })?; - - file_content = Some(bytes.to_vec()); - } - _ => {} + let (version, definition) = match &request.definition { + Some(definition) => { + let encrypted = crypto.encrypt_json(existing.workspace_id, definition)?; + (Some(definition.version.to_string()), Some(encrypted)) } - } + None => (None, None), + }; - let mut updates = UpdateWorkspaceContext { - name, - description, + let updates = UpdateWorkspaceContext { + name: request.name, + description: request.description, + version, + definition, ..Default::default() }; - // If file content was provided, encrypt and store new content - if let Some(content) = file_content { - let encrypted_content = crypto.encrypt(existing.workspace_id, &content)?; - - let context_key = ContextKey::new(existing.workspace_id, existing.id); - let context_store = nats_client - .object_store::() - .await?; - - let reader = Cursor::new(encrypted_content); - let put_result = context_store.put(&context_key, reader).await?; - - updates.storage_key = Some(context_key.to_string()); - updates.content_size = Some(put_result.size() as i64); - updates.content_hash = Some(put_result.sha256().to_vec()); - } - let context = conn .update_workspace_context(path_params.context_id, updates) .await?; tracing::info!(target: TRACING_TARGET, "Context updated"); - Ok((StatusCode::OK, Json(Context::from_model(context)))) + Ok((StatusCode::OK, Json(Context::from_model(context, &crypto)?))) } fn update_context_docs(op: TransformOperation) -> TransformOperation { op.summary("Update context") - .description( - "Updates context metadata and optionally replaces the content via multipart upload. \ - All fields are optional. If a 'file' field is provided, the content is re-encrypted \ - and stored.", - ) + .description("Updates context fields. Replacing the definition replaces the whole body.") .response::<200, Json>() .response::<400, Json>() .response::<401, Json>() @@ -398,7 +256,6 @@ fn update_context_docs(op: TransformOperation) -> TransformOperation { )] async fn delete_context( State(pg_client): State, - State(nats_client): State, AuthState(auth_state): AuthState, Path(path_params): Path, ) -> Result { @@ -412,20 +269,6 @@ async fn delete_context( .authorize_workspace(&mut conn, context.workspace_id, Permission::ManageContexts) .await?; - // Delete the object from NATS (best effort, context may already be gone) - let context_store = nats_client - .object_store::() - .await?; - let context_key = ContextKey::new(context.workspace_id, context.id); - if let Err(err) = context_store.delete(&context_key).await { - tracing::warn!( - target: TRACING_TARGET, - error = %err, - context_id = %path_params.context_id, - "Failed to delete context object from NATS (proceeding with soft delete)" - ); - } - conn.delete_workspace_context(path_params.context_id) .await?; @@ -436,7 +279,7 @@ async fn delete_context( fn delete_context_docs(op: TransformOperation) -> TransformOperation { op.summary("Delete context") - .description("Soft-deletes the context from the workspace and removes the encrypted content from NATS.") + .description("Soft-deletes the context from the workspace.") .response::<204, ()>() .response::<401, Json>() .response::<403, Json>() @@ -458,14 +301,12 @@ pub fn routes() -> ApiRouter { .api_route( "/workspaces/{workspaceId}/contexts/", post_with(create_context, create_context_docs) - .layer(DefaultBodyLimit::max(DEFAULT_MAX_FILE_BODY_SIZE)) .get_with(list_contexts, list_contexts_docs), ) .api_route( "/contexts/{contextId}/", get_with(read_context, read_context_docs) .put_with(update_context, update_context_docs) - .layer(DefaultBodyLimit::max(DEFAULT_MAX_FILE_BODY_SIZE)) .delete_with(delete_context, delete_context_docs), ) .with_path_items(|item| item.tag("Contexts")) diff --git a/crates/nvisy-server/src/handler/mod.rs b/crates/nvisy-server/src/handler/mod.rs index b283ab6..aeedc5e 100644 --- a/crates/nvisy-server/src/handler/mod.rs +++ b/crates/nvisy-server/src/handler/mod.rs @@ -16,6 +16,7 @@ mod monitors; mod notifications; mod pipeline_runs; mod pipelines; +mod policies; pub mod request; pub mod response; mod tokens; @@ -55,6 +56,7 @@ fn private_routes( .merge(annotations::routes()) .merge(pipelines::routes()) .merge(pipeline_runs::routes()) + .merge(policies::routes()) .merge(notifications::routes()); if let Some(additional) = additional_routes { diff --git a/crates/nvisy-server/src/handler/policies.rs b/crates/nvisy-server/src/handler/policies.rs new file mode 100644 index 0000000..b289421 --- /dev/null +++ b/crates/nvisy-server/src/handler/policies.rs @@ -0,0 +1,312 @@ +//! Workspace policy management handlers. +//! +//! Policies are structured redaction governance documents (the engine's +//! Policy type) consumed by the redaction pipeline. The definition is +//! validated against the schema, then stored encrypted (XChaCha20-Poly1305, +//! workspace-derived key) as a BYTEA column in PostgreSQL, scoped to a +//! workspace. + +use aide::axum::ApiRouter; +use aide::transform::TransformOperation; +use axum::extract::State; +use axum::http::StatusCode; +use nvisy_postgres::model::{NewWorkspacePolicy, UpdateWorkspacePolicy, WorkspacePolicy}; +use nvisy_postgres::query::WorkspacePolicyRepository; +use nvisy_postgres::{PgClient, PgConn}; +use uuid::Uuid; + +use crate::extract::{AuthProvider, AuthState, Json, Path, Permission, Query, ValidateJson}; +use crate::handler::request::{ + CreatePolicy, CursorPagination, PolicyPathParams, UpdatePolicy, WorkspacePathParams, +}; +use crate::handler::response::{ErrorResponse, PoliciesPage, Policy}; +use crate::handler::{Error, Result}; +use crate::service::{CryptoService, ServiceState}; + +/// Tracing target for workspace policy operations. +const TRACING_TARGET: &str = "nvisy_server::handler::policies"; + +/// Creates a new workspace policy. +/// +/// The request body carries a structured policy definition; its name, +/// description, and version drive the stored record unless overridden. +/// Requires `ManagePolicies` permission for the workspace. +#[tracing::instrument( + skip_all, + fields( + account_id = %auth_state.account_id, + workspace_id = %path_params.workspace_id, + ) +)] +async fn create_policy( + State(pg_client): State, + State(crypto): State, + AuthState(auth_state): AuthState, + Path(path_params): Path, + ValidateJson(request): ValidateJson, +) -> Result<(StatusCode, Json)> { + tracing::debug!(target: TRACING_TARGET, "Creating workspace policy"); + + let mut conn = pg_client.get_connection().await?; + + auth_state + .authorize_workspace( + &mut conn, + path_params.workspace_id, + Permission::ManagePolicies, + ) + .await?; + + let definition = &request.definition; + let name = request.name.unwrap_or_else(|| definition.name.to_string()); + let description = request + .description + .or_else(|| definition.description.clone()); + let version = definition.version.to_string(); + let encrypted = crypto.encrypt_json(path_params.workspace_id, definition)?; + + let new_policy = NewWorkspacePolicy { + workspace_id: path_params.workspace_id, + account_id: auth_state.account_id, + name, + description, + version, + definition: encrypted, + metadata: None, + }; + + let policy = conn.create_workspace_policy(new_policy).await?; + + tracing::info!(target: TRACING_TARGET, policy_id = %policy.id, "Policy created"); + + Ok(( + StatusCode::CREATED, + Json(Policy::from_model(policy, &crypto)?), + )) +} + +fn create_policy_docs(op: TransformOperation) -> TransformOperation { + op.summary("Create policy") + .description("Creates a structured redaction policy for the workspace.") + .response::<201, Json>() + .response::<400, Json>() + .response::<401, Json>() + .response::<403, Json>() +} + +/// Lists all policies for a workspace. +#[tracing::instrument( + skip_all, + fields( + account_id = %auth_state.account_id, + workspace_id = %path_params.workspace_id, + ) +)] +async fn list_policies( + State(pg_client): State, + State(crypto): State, + AuthState(auth_state): AuthState, + Path(path_params): Path, + Query(pagination): Query, +) -> Result<(StatusCode, Json)> { + tracing::debug!(target: TRACING_TARGET, "Listing workspace policies"); + + let mut conn = pg_client.get_connection().await?; + + auth_state + .authorize_workspace( + &mut conn, + path_params.workspace_id, + Permission::ViewPolicies, + ) + .await?; + + let page = conn + .cursor_list_workspace_policies(path_params.workspace_id, pagination.into()) + .await?; + + tracing::debug!( + target: TRACING_TARGET, + policy_count = page.items.len(), + "Workspace policies listed", + ); + + let page = + PoliciesPage::try_from_cursor_page(page, |model| Policy::from_model(model, &crypto))?; + + Ok((StatusCode::OK, Json(page))) +} + +fn list_policies_docs(op: TransformOperation) -> TransformOperation { + op.summary("List policies") + .description("Returns all policies for the workspace.") + .response::<200, Json>() + .response::<401, Json>() + .response::<403, Json>() +} + +/// Retrieves a specific workspace policy. +#[tracing::instrument( + skip_all, + fields( + account_id = %auth_state.account_id, + policy_id = %path_params.policy_id, + ) +)] +async fn read_policy( + State(pg_client): State, + State(crypto): State, + AuthState(auth_state): AuthState, + Path(path_params): Path, +) -> Result<(StatusCode, Json)> { + tracing::debug!(target: TRACING_TARGET, "Reading workspace policy"); + + let mut conn = pg_client.get_connection().await?; + + let policy = find_policy(&mut conn, path_params.policy_id).await?; + + auth_state + .authorize_workspace(&mut conn, policy.workspace_id, Permission::ViewPolicies) + .await?; + + tracing::debug!(target: TRACING_TARGET, "Workspace policy read"); + + Ok((StatusCode::OK, Json(Policy::from_model(policy, &crypto)?))) +} + +fn read_policy_docs(op: TransformOperation) -> TransformOperation { + op.summary("Get policy") + .description("Returns a single policy.") + .response::<200, Json>() + .response::<401, Json>() + .response::<403, Json>() + .response::<404, Json>() +} + +/// Updates a workspace policy. +/// +/// All fields are optional; replacing the definition replaces the whole +/// policy body (and its version). Requires `ManagePolicies` permission. +#[tracing::instrument( + skip_all, + fields( + account_id = %auth_state.account_id, + policy_id = %path_params.policy_id, + ) +)] +async fn update_policy( + State(pg_client): State, + State(crypto): State, + AuthState(auth_state): AuthState, + Path(path_params): Path, + ValidateJson(request): ValidateJson, +) -> Result<(StatusCode, Json)> { + tracing::debug!(target: TRACING_TARGET, "Updating workspace policy"); + + let mut conn = pg_client.get_connection().await?; + + let existing = find_policy(&mut conn, path_params.policy_id).await?; + + auth_state + .authorize_workspace(&mut conn, existing.workspace_id, Permission::ManagePolicies) + .await?; + + let (version, definition) = match &request.definition { + Some(definition) => { + let encrypted = crypto.encrypt_json(existing.workspace_id, definition)?; + (Some(definition.version.to_string()), Some(encrypted)) + } + None => (None, None), + }; + + let updates = UpdateWorkspacePolicy { + name: request.name, + description: request.description, + version, + definition, + ..Default::default() + }; + + let policy = conn + .update_workspace_policy(path_params.policy_id, updates) + .await?; + + tracing::info!(target: TRACING_TARGET, "Policy updated"); + + Ok((StatusCode::OK, Json(Policy::from_model(policy, &crypto)?))) +} + +fn update_policy_docs(op: TransformOperation) -> TransformOperation { + op.summary("Update policy") + .description("Updates policy fields. Replacing the definition replaces the whole body.") + .response::<200, Json>() + .response::<400, Json>() + .response::<401, Json>() + .response::<403, Json>() + .response::<404, Json>() +} + +/// Deletes a workspace policy. +#[tracing::instrument( + skip_all, + fields( + account_id = %auth_state.account_id, + policy_id = %path_params.policy_id, + ) +)] +async fn delete_policy( + State(pg_client): State, + AuthState(auth_state): AuthState, + Path(path_params): Path, +) -> Result { + tracing::debug!(target: TRACING_TARGET, "Deleting workspace policy"); + + let mut conn = pg_client.get_connection().await?; + + let policy = find_policy(&mut conn, path_params.policy_id).await?; + + auth_state + .authorize_workspace(&mut conn, policy.workspace_id, Permission::ManagePolicies) + .await?; + + conn.delete_workspace_policy(path_params.policy_id).await?; + + tracing::info!(target: TRACING_TARGET, "Policy deleted"); + + Ok(StatusCode::NO_CONTENT) +} + +fn delete_policy_docs(op: TransformOperation) -> TransformOperation { + op.summary("Delete policy") + .description("Soft-deletes the policy from the workspace.") + .response::<204, ()>() + .response::<401, Json>() + .response::<403, Json>() + .response::<404, Json>() +} + +/// Finds a policy by ID or returns NotFound error. +async fn find_policy(conn: &mut PgConn, policy_id: Uuid) -> Result { + conn.find_workspace_policy_by_id(policy_id) + .await? + .ok_or_else(|| Error::not_found("policy")) +} + +/// Returns routes for workspace policy management. +pub fn routes() -> ApiRouter { + use aide::axum::routing::*; + + ApiRouter::new() + .api_route( + "/workspaces/{workspaceId}/policies/", + post_with(create_policy, create_policy_docs) + .get_with(list_policies, list_policies_docs), + ) + .api_route( + "/policies/{policyId}/", + get_with(read_policy, read_policy_docs) + .put_with(update_policy, update_policy_docs) + .delete_with(delete_policy, delete_policy_docs), + ) + .with_path_items(|item| item.tag("Policies")) +} diff --git a/crates/nvisy-server/src/handler/request/contexts.rs b/crates/nvisy-server/src/handler/request/contexts.rs index 45fe275..70fbcc9 100644 --- a/crates/nvisy-server/src/handler/request/contexts.rs +++ b/crates/nvisy-server/src/handler/request/contexts.rs @@ -1,5 +1,6 @@ //! Context request types. +use nvisy_schema::context::Context as SchemaContext; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -14,7 +15,25 @@ pub struct ContextPathParams { pub context_id: Uuid, } +/// Request payload for creating a new workspace context. +/// +/// The `definition` is a structured context the redaction engine consumes; +/// its `name`, `description`, and `version` drive the stored columns unless +/// overridden here. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Validate)] +#[serde(rename_all = "camelCase")] +pub struct CreateContext { + /// Optional display name override. Defaults to the context's own name. + pub name: Option, + /// Optional description override. Defaults to the context's own description. + pub description: Option, + /// The structured context body consumed by the engine. + pub definition: SchemaContext, +} + /// Request payload for updating an existing workspace context. +/// +/// Replacing the `definition` replaces the whole context body. #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Validate)] #[serde(rename_all = "camelCase")] pub struct UpdateContext { @@ -24,4 +43,6 @@ pub struct UpdateContext { /// Context description. #[validate(length(max = 4096))] pub description: Option>, + /// New context body (replaces the stored definition). + pub definition: Option, } diff --git a/crates/nvisy-server/src/handler/request/mod.rs b/crates/nvisy-server/src/handler/request/mod.rs index 6662607..a086e38 100644 --- a/crates/nvisy-server/src/handler/request/mod.rs +++ b/crates/nvisy-server/src/handler/request/mod.rs @@ -12,6 +12,7 @@ mod monitors; mod paginations; mod paths; mod pipelines; +mod policies; mod tokens; mod validations; mod webhooks; @@ -29,6 +30,7 @@ pub use monitors::*; pub use paginations::*; pub use paths::*; pub use pipelines::*; +pub use policies::*; pub use tokens::*; pub use validations::*; pub use webhooks::*; diff --git a/crates/nvisy-server/src/handler/request/policies.rs b/crates/nvisy-server/src/handler/request/policies.rs new file mode 100644 index 0000000..14fe756 --- /dev/null +++ b/crates/nvisy-server/src/handler/request/policies.rs @@ -0,0 +1,48 @@ +//! Policy request types. + +use nvisy_schema::policy::Policy as SchemaPolicy; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; +use validator::Validate; + +/// Path parameters for policy operations. +#[must_use] +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct PolicyPathParams { + /// Unique identifier of the policy. + pub policy_id: Uuid, +} + +/// Request payload for creating a new workspace policy. +/// +/// The `definition` is a structured policy the redaction engine consumes; +/// its `name`, `description`, and `version` drive the stored columns unless +/// overridden here. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Validate)] +#[serde(rename_all = "camelCase")] +pub struct CreatePolicy { + /// Optional display name override. Defaults to the policy's own name. + pub name: Option, + /// Optional description override. Defaults to the policy's own description. + pub description: Option, + /// The structured policy body consumed by the engine. + pub definition: SchemaPolicy, +} + +/// Request payload for updating an existing workspace policy. +/// +/// Replacing the `definition` replaces the whole policy body. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Validate)] +#[serde(rename_all = "camelCase")] +pub struct UpdatePolicy { + /// Human-readable policy name. + #[validate(length(min = 1, max = 255))] + pub name: Option, + /// Policy description. + #[validate(length(max = 4096))] + pub description: Option>, + /// New policy body (replaces the stored definition). + pub definition: Option, +} diff --git a/crates/nvisy-server/src/handler/response/contexts.rs b/crates/nvisy-server/src/handler/response/contexts.rs index ffed2b0..ff649d3 100644 --- a/crates/nvisy-server/src/handler/response/contexts.rs +++ b/crates/nvisy-server/src/handler/response/contexts.rs @@ -2,16 +2,15 @@ use jiff::Timestamp; use nvisy_postgres::model::WorkspaceContext; +use nvisy_schema::context::Context as SchemaContext; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; use super::Page; +use crate::service::CryptoService; /// Response type for a workspace context. -/// -/// Note: The encrypted content is stored in NATS and never exposed -/// in API responses. Only metadata is returned. #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct Context { @@ -26,10 +25,10 @@ pub struct Context { /// Context description. #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, - /// Content MIME type. - pub mime_type: String, - /// Size of the content in bytes. - pub content_size: i64, + /// Semver of the context body. + pub version: String, + /// The structured context body consumed by the engine. + pub definition: SchemaContext, /// When the context was created. pub created_at: Timestamp, /// When the context was last updated. @@ -40,18 +39,24 @@ pub struct Context { pub type ContextsPage = Page; impl Context { - /// Creates a response from a database model. - pub fn from_model(context: WorkspaceContext) -> Self { - Self { + /// Creates a response from a database model, decrypting the definition. + pub fn from_model( + context: WorkspaceContext, + crypto: &CryptoService, + ) -> crate::handler::Result { + let definition = + crypto.decrypt_json::(context.workspace_id, &context.definition)?; + + Ok(Self { id: context.id, workspace_id: context.workspace_id, account_id: context.account_id, name: context.name, description: context.description, - mime_type: context.mime_type, - content_size: context.content_size, + version: context.version, + definition, created_at: context.created_at.into(), updated_at: context.updated_at.into(), - } + }) } } diff --git a/crates/nvisy-server/src/handler/response/mod.rs b/crates/nvisy-server/src/handler/response/mod.rs index 185e886..7ad5e8d 100644 --- a/crates/nvisy-server/src/handler/response/mod.rs +++ b/crates/nvisy-server/src/handler/response/mod.rs @@ -19,6 +19,7 @@ mod monitors; mod notifications; mod pipeline_runs; mod pipelines; +mod policies; mod tokens; mod webhooks; mod workspaces; @@ -38,6 +39,7 @@ pub use monitors::*; pub use notifications::*; pub use pipeline_runs::*; pub use pipelines::*; +pub use policies::*; pub use tokens::*; pub use webhooks::*; pub use workspaces::*; @@ -108,4 +110,19 @@ impl Page { next_cursor: page.next_cursor, } } + + /// Creates a page from a cursor page, mapping items with a fallible function. + /// + /// Returns the first error encountered while mapping (e.g. a decryption + /// failure), otherwise the fully mapped page. + pub fn try_from_cursor_page(page: CursorPage, f: F) -> Result + where + F: FnMut(M) -> Result, + { + Ok(Self { + items: page.items.into_iter().map(f).collect::>()?, + total: page.total, + next_cursor: page.next_cursor, + }) + } } diff --git a/crates/nvisy-server/src/handler/response/policies.rs b/crates/nvisy-server/src/handler/response/policies.rs new file mode 100644 index 0000000..c2e0f90 --- /dev/null +++ b/crates/nvisy-server/src/handler/response/policies.rs @@ -0,0 +1,62 @@ +//! Policy response types. + +use jiff::Timestamp; +use nvisy_postgres::model::WorkspacePolicy; +use nvisy_schema::policy::Policy as SchemaPolicy; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use super::Page; +use crate::service::CryptoService; + +/// Response type for a workspace policy. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct Policy { + /// Unique policy identifier. + pub id: Uuid, + /// Workspace this policy belongs to. + pub workspace_id: Uuid, + /// Account that created this policy. + pub account_id: Uuid, + /// Human-readable policy name. + pub name: String, + /// Policy description. + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + /// Semver of the policy body. + pub version: String, + /// The structured policy body consumed by the engine. + pub definition: SchemaPolicy, + /// When the policy was created. + pub created_at: Timestamp, + /// When the policy was last updated. + pub updated_at: Timestamp, +} + +/// Paginated list of policies. +pub type PoliciesPage = Page; + +impl Policy { + /// Creates a response from a database model, decrypting the definition. + pub fn from_model( + policy: WorkspacePolicy, + crypto: &CryptoService, + ) -> crate::handler::Result { + let definition = + crypto.decrypt_json::(policy.workspace_id, &policy.definition)?; + + Ok(Self { + id: policy.id, + workspace_id: policy.workspace_id, + account_id: policy.account_id, + name: policy.name, + description: policy.description, + version: policy.version, + definition, + created_at: policy.created_at.into(), + updated_at: policy.updated_at.into(), + }) + } +} diff --git a/migrations/2026-01-19-045012_pipelines/up.sql b/migrations/2026-01-19-045012_pipelines/up.sql index 4424939..48ab2ac 100644 --- a/migrations/2026-01-19-045012_pipelines/up.sql +++ b/migrations/2026-01-19-045012_pipelines/up.sql @@ -111,10 +111,11 @@ CREATE TABLE workspace_policies ( CONSTRAINT workspace_policies_version_length CHECK (length(trim(version)) BETWEEN 1 AND 64), -- Policy body (nvisy_schema::policy::Policy as JSON: rules, labels, - -- fallback, retention, applies_when predicate). - definition JSONB NOT NULL, + -- fallback, retention, applies_when predicate). Stored XChaCha20-Poly1305 + -- encrypted with the workspace-derived key. + definition BYTEA NOT NULL, - CONSTRAINT workspace_policies_definition_size CHECK (length(definition::TEXT) BETWEEN 2 AND 1048576), + CONSTRAINT workspace_policies_definition_size CHECK (length(definition) BETWEEN 1 AND 1048576), -- Metadata (for filtering/display) metadata JSONB NOT NULL DEFAULT '{}', @@ -156,7 +157,7 @@ COMMENT ON COLUMN workspace_policies.account_id IS 'Creator account reference'; COMMENT ON COLUMN workspace_policies.name IS 'Human-readable policy name (1-255 chars)'; COMMENT ON COLUMN workspace_policies.description IS 'Policy description (up to 4096 chars)'; COMMENT ON COLUMN workspace_policies.version IS 'Semver of the policy body'; -COMMENT ON COLUMN workspace_policies.definition IS 'Policy body as JSON (rules, labels, fallback, retention)'; +COMMENT ON COLUMN workspace_policies.definition IS 'Encrypted policy body (XChaCha20-Poly1305, workspace-derived key)'; COMMENT ON COLUMN workspace_policies.metadata IS 'Metadata for filtering/display'; COMMENT ON COLUMN workspace_policies.created_at IS 'Creation timestamp'; COMMENT ON COLUMN workspace_policies.updated_at IS 'Last modification timestamp'; @@ -183,9 +184,10 @@ CREATE TABLE workspace_contexts ( -- Context body (nvisy_schema::context::Context as JSON: typed -- reference-data entries — biometric, geospatial, temporal, ...). - definition JSONB NOT NULL, + -- Stored XChaCha20-Poly1305 encrypted with the workspace-derived key. + definition BYTEA NOT NULL, - CONSTRAINT workspace_contexts_definition_size CHECK (length(definition::TEXT) BETWEEN 2 AND 1048576), + CONSTRAINT workspace_contexts_definition_size CHECK (length(definition) BETWEEN 1 AND 1048576), -- Metadata (for filtering/display) metadata JSONB NOT NULL DEFAULT '{}', @@ -227,7 +229,7 @@ COMMENT ON COLUMN workspace_contexts.account_id IS 'Creator account reference'; COMMENT ON COLUMN workspace_contexts.name IS 'Human-readable context name (1-255 chars)'; COMMENT ON COLUMN workspace_contexts.description IS 'Context description (up to 4096 chars)'; COMMENT ON COLUMN workspace_contexts.version IS 'Semver of the context body'; -COMMENT ON COLUMN workspace_contexts.definition IS 'Context body as JSON (typed reference-data entries)'; +COMMENT ON COLUMN workspace_contexts.definition IS 'Encrypted context body (XChaCha20-Poly1305, workspace-derived key)'; COMMENT ON COLUMN workspace_contexts.metadata IS 'Metadata for filtering/display'; COMMENT ON COLUMN workspace_contexts.created_at IS 'Creation timestamp'; COMMENT ON COLUMN workspace_contexts.updated_at IS 'Last modification timestamp'; From f0fa6c0ebcdb6ef1f69b2151afe2d3de7e95f8d5 Mon Sep 17 00:00:00 2001 From: Oleh Martsokha Date: Fri, 3 Jul 2026 22:24:01 +0200 Subject: [PATCH 5/7] chore(deny): allow the engine's git deps and licenses Adding nvisy-engine/nvisy-schema (and transitively elide) as git dependencies surfaces new deny findings: - sources: allow the nvisycom/runtime and nvisycom/elide git repos. - licenses: allow MPL-2.0 (cssparser/selectors/symphonia); scope an LGPL-3.0 exception to mp3lame-encoder/-sys only, mirroring elide's carve-out for the opt-in mp3 codec. - bans: allow-wildcard-paths for the versionless first-party git deps. - advisories: ignore two advisory-only unmaintained crates pulled in transitively by elide-codec (paste, ttf-parser). Co-Authored-By: Claude Opus 4.8 (1M context) --- deny.toml | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/deny.toml b/deny.toml index 8d8b53e..28793d5 100644 --- a/deny.toml +++ b/deny.toml @@ -15,7 +15,7 @@ targets = [ db-path = "~/.cargo/advisory-db" # The url(s) of the advisory databases to use db-urls = ["https://github.com/rustsec/advisory-db"] -# The lint level for unmaintained crates +# The lint level for unmaintained crates. unmaintained = "all" # The lint level for crates that have been yanked from their source registry yanked = "deny" @@ -30,6 +30,12 @@ ignore = [ # yet. Remove once object_store ships a quick-xml >=0.41. "RUSTSEC-2026-0194", "RUSTSEC-2026-0195", + # Unmaintained crates pulled in transitively by the engine's codec stack + # (elide-codec): paste via its macros, ttf-parser via ab_glyph/imageproc + # text rendering. Both are advisory-only (not vulnerabilities) and vetted + # upstream in elide. Remove if elide drops them. + "RUSTSEC-2024-0436", + "RUSTSEC-2026-0192", ] [licenses] @@ -57,16 +63,31 @@ allow = [ "OpenSSL", "bzip2-1.0.6", "CDLA-Permissive-2.0", + # Weak copyleft; pulled in transitively by the engine's HTML codec + # (cssparser, selectors) and audio codecs (symphonia). File-level + # copyleft only — using these crates as-is does not affect our source. + "MPL-2.0", ] -# For compound licenses, we'll be permissive and only block if they contain denied licenses -exceptions = [] +# LGPL-3.0, accepted only for the engine's opt-in `mp3` codec feature. +# MP3 *encoding* has no permissively-licensed, production-quality alternative +# in the ecosystem — LAME (libmp3lame) is effectively the only option, and +# every substitute (incl. Shine) is also LGPL. The obligation is satisfiable +# for proprietary use via dynamic/separate linking. Mirrors the carve-out in +# the upstream elide repository. +exceptions = [ + { name = "mp3lame-encoder", allow = ["LGPL-3.0"] }, + { name = "mp3lame-sys", allow = ["LGPL-3.0"] }, +] [bans] # Lint level for when multiple versions of the same crate are detected multiple-versions = "warn" # Lint level for when a crate version requirement is `*` wildcards = "deny" +# First-party git/path dependencies (nvisy-engine, nvisy-schema) are pinned by +# git ref, not a version requirement, so their `*` is not a real wildcard. +allow-wildcard-paths = true # The graph highlighting used when creating dotgraphs for crates with multiple versions highlight = "all" @@ -92,4 +113,7 @@ unknown-git = "deny" # List of URLs for allowed crate registries allow-registry = ["https://github.com/rust-lang/crates.io-index"] # List of URLs for allowed Git repositories -allow-git = [] +allow-git = [ + "https://github.com/nvisycom/runtime", + "https://github.com/nvisycom/elide", +] From f592e4d17b475e7f4f6681edb4f87ecd796b91f5 Mon Sep 17 00:00:00 2001 From: Oleh Martsokha Date: Fri, 3 Jul 2026 23:55:53 +0200 Subject: [PATCH 6/7] chore: pull elide/runtime, drop now-unneeded deny carve-outs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upstream elide update drops the MP3 (mp3lame, LGPL-3.0) codec and the ttf-parser text-rendering path, so those deny carve-outs are no longer needed: - remove the LGPL-3.0 exception (mp3lame gone from the tree) - drop the ttf-parser + paste unmaintained ignores (no longer pulled in) MPL-2.0 stays (symphonia/cssparser still present). The remaining ignores (proc-macro-error2 via validator; quick-xml via object_store) are our own crates.io deps with no released fix yet — the quick-xml bump is merged upstream (arrow-rs-object-store#785) but unreleased. Co-Authored-By: Claude Opus 4.8 (1M context) --- Cargo.lock | 187 ++++++----------------------------------------------- deny.toml | 24 ++----- 2 files changed, 23 insertions(+), 188 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3bc2742..ad37f6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,22 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "ab_glyph" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" -dependencies = [ - "ab_glyph_rasterizer", - "owned_ttf_parser", -] - -[[package]] -name = "ab_glyph_rasterizer" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" - [[package]] name = "adler2" version = "2.0.1" @@ -346,15 +330,6 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" -[[package]] -name = "autotools" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef941527c41b0fc0dd48511a8154cd5fc7e29200a0ff8b7203c5d777dbc795cf" -dependencies = [ - "cc", -] - [[package]] name = "av-scenechange" version = "0.14.1" @@ -929,12 +904,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "colorchoice" version = "1.0.5" @@ -1712,7 +1681,7 @@ checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "elide" version = "0.1.0" -source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +source = "git+https://github.com/nvisycom/elide?branch=main#5bc8557ff10c0d9a28413a11a932981582f16314" dependencies = [ "async-trait", "elide-codec", @@ -1730,7 +1699,7 @@ dependencies = [ [[package]] name = "elide-bento" version = "0.1.0" -source = "git+https://github.com/nvisycom/runtime?branch=main#a85d6feed24f58bd0e20e4cdbfd8912bc3e0797e" +source = "git+https://github.com/nvisycom/runtime?branch=main#d993014f2a1bae6bb0c3738ffcdde59e3378528b" dependencies = [ "async-trait", "base64", @@ -1746,7 +1715,7 @@ dependencies = [ [[package]] name = "elide-codec" version = "0.1.0" -source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +source = "git+https://github.com/nvisycom/elide?branch=main#5bc8557ff10c0d9a28413a11a932981582f16314" dependencies = [ "async-trait", "bytes", @@ -1757,7 +1726,6 @@ dependencies = [ "hound", "image", "imageproc", - "mp3lame-encoder", "quick-xml 0.41.0", "scraper", "serde_json", @@ -1770,7 +1738,7 @@ dependencies = [ [[package]] name = "elide-context" version = "0.1.0" -source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +source = "git+https://github.com/nvisycom/elide?branch=main#5bc8557ff10c0d9a28413a11a932981582f16314" dependencies = [ "async-trait", "elide-core", @@ -1781,7 +1749,7 @@ dependencies = [ [[package]] name = "elide-core" version = "0.1.0" -source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +source = "git+https://github.com/nvisycom/elide?branch=main#5bc8557ff10c0d9a28413a11a932981582f16314" dependencies = [ "async-trait", "bytes", @@ -1799,7 +1767,7 @@ dependencies = [ [[package]] name = "elide-detection" version = "0.1.0" -source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +source = "git+https://github.com/nvisycom/elide?branch=main#5bc8557ff10c0d9a28413a11a932981582f16314" dependencies = [ "async-trait", "elide-core", @@ -1812,7 +1780,7 @@ dependencies = [ [[package]] name = "elide-lingua" version = "0.1.0" -source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +source = "git+https://github.com/nvisycom/elide?branch=main#5bc8557ff10c0d9a28413a11a932981582f16314" dependencies = [ "async-trait", "elide-core", @@ -1823,7 +1791,7 @@ dependencies = [ [[package]] name = "elide-llm" version = "0.1.0" -source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +source = "git+https://github.com/nvisycom/elide?branch=main#5bc8557ff10c0d9a28413a11a932981582f16314" dependencies = [ "async-trait", "derive_builder", @@ -1843,7 +1811,7 @@ dependencies = [ [[package]] name = "elide-ner" version = "0.1.0" -source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +source = "git+https://github.com/nvisycom/elide?branch=main#5bc8557ff10c0d9a28413a11a932981582f16314" dependencies = [ "async-trait", "derive_builder", @@ -1857,7 +1825,7 @@ dependencies = [ [[package]] name = "elide-ocr" version = "0.1.0" -source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +source = "git+https://github.com/nvisycom/elide?branch=main#5bc8557ff10c0d9a28413a11a932981582f16314" dependencies = [ "async-trait", "elide-core", @@ -1867,7 +1835,7 @@ dependencies = [ [[package]] name = "elide-orchestration" version = "0.1.0" -source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +source = "git+https://github.com/nvisycom/elide?branch=main#5bc8557ff10c0d9a28413a11a932981582f16314" dependencies = [ "bytes", "elide-codec", @@ -1881,7 +1849,7 @@ dependencies = [ [[package]] name = "elide-pattern" version = "0.1.0" -source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +source = "git+https://github.com/nvisycom/elide?branch=main#5bc8557ff10c0d9a28413a11a932981582f16314" dependencies = [ "aho-corasick", "async-trait", @@ -1901,7 +1869,7 @@ dependencies = [ [[package]] name = "elide-redaction" version = "0.1.0" -source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +source = "git+https://github.com/nvisycom/elide?branch=main#5bc8557ff10c0d9a28413a11a932981582f16314" dependencies = [ "async-trait", "bytes", @@ -1916,7 +1884,7 @@ dependencies = [ [[package]] name = "elide-stt" version = "0.1.0" -source = "git+https://github.com/nvisycom/elide?branch=main#39e3acc451fa1d3ab30ac4401de3b87e130dab8f" +source = "git+https://github.com/nvisycom/elide?branch=main#5bc8557ff10c0d9a28413a11a932981582f16314" dependencies = [ "async-trait", "elide-core", @@ -2343,16 +2311,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gif" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "glam" version = "0.30.10" @@ -2859,39 +2817,23 @@ checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" dependencies = [ "bytemuck", "byteorder-lite", - "color_quant", "exr", - "gif", - "image-webp", "moxcms", "num-traits", "png", - "qoi", "ravif", "rayon", - "rgb", "tiff", "zune-core", "zune-jpeg", ] -[[package]] -name = "image-webp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" -dependencies = [ - "byteorder-lite", - "quick-error", -] - [[package]] name = "imageproc" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7b27bc0867dc40df08deb53d6e96342db6e0702e7ae33ed09a4eba33e594b05" dependencies = [ - "ab_glyph", "approx", "getrandom 0.4.2", "image", @@ -2901,7 +2843,6 @@ dependencies = [ "rand 0.10.1", "rand_distr", "rayon", - "rustdct", ] [[package]] @@ -3468,26 +3409,6 @@ dependencies = [ "pxfm", ] -[[package]] -name = "mp3lame-encoder" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a3bb37607c4df224a0b7316897493fb49bd27312cb1b399fc4a43d2d7f2d3d8" -dependencies = [ - "mp3lame-sys", -] - -[[package]] -name = "mp3lame-sys" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e3b1772db47828840702e5a2e05694527f731abadf9b931355d54035f019d8" -dependencies = [ - "autotools", - "cc", - "libc", -] - [[package]] name = "multer" version = "3.1.0" @@ -3753,7 +3674,7 @@ dependencies = [ [[package]] name = "nvisy-core" version = "0.1.0" -source = "git+https://github.com/nvisycom/runtime?branch=main#a85d6feed24f58bd0e20e4cdbfd8912bc3e0797e" +source = "git+https://github.com/nvisycom/runtime?branch=main#d993014f2a1bae6bb0c3738ffcdde59e3378528b" dependencies = [ "derive_builder", "derive_more", @@ -3768,7 +3689,7 @@ dependencies = [ [[package]] name = "nvisy-engine" version = "0.1.0" -source = "git+https://github.com/nvisycom/runtime?branch=main#a85d6feed24f58bd0e20e4cdbfd8912bc3e0797e" +source = "git+https://github.com/nvisycom/runtime?branch=main#d993014f2a1bae6bb0c3738ffcdde59e3378528b" dependencies = [ "bytes", "elide", @@ -3857,7 +3778,7 @@ dependencies = [ [[package]] name = "nvisy-schema" version = "0.1.0" -source = "git+https://github.com/nvisycom/runtime?branch=main#a85d6feed24f58bd0e20e4cdbfd8912bc3e0797e" +source = "git+https://github.com/nvisycom/runtime?branch=main#d993014f2a1bae6bb0c3738ffcdde59e3378528b" dependencies = [ "bytes", "derive_builder", @@ -4050,15 +3971,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "owned_ttf_parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" -dependencies = [ - "ttf-parser", -] - [[package]] name = "oxilangtag" version = "0.1.6" @@ -4233,7 +4145,7 @@ dependencies = [ "nom 7.1.3", "once_cell", "postcard", - "quick-xml 0.40.1", + "quick-xml 0.41.0", "regex", "regex-cache", "serde", @@ -4432,15 +4344,6 @@ dependencies = [ "syn", ] -[[package]] -name = "primal-check" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" -dependencies = [ - "num-integer", -] - [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -4516,15 +4419,6 @@ version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - [[package]] name = "quick-error" version = "2.0.1" @@ -5097,29 +4991,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustdct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b61555105d6a9bf98797c063c362a1d24ed8ab0431655e38f1cf51e52089551" -dependencies = [ - "rustfft", -] - -[[package]] -name = "rustfft" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21db5f9893e91f41798c88680037dba611ca6674703c1a18601b01a72c8adb89" -dependencies = [ - "num-complex", - "num-integer", - "num-traits", - "primal-check", - "strength_reduce", - "transpose", -] - [[package]] name = "rustix" version = "1.1.4" @@ -5709,12 +5580,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" -[[package]] -name = "strength_reduce" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" - [[package]] name = "string_cache" version = "0.9.0" @@ -6401,16 +6266,6 @@ dependencies = [ "tracing-serde", ] -[[package]] -name = "transpose" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" -dependencies = [ - "num-integer", - "strength_reduce", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -6427,12 +6282,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "ttf-parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" - [[package]] name = "tungstenite" version = "0.28.0" diff --git a/deny.toml b/deny.toml index 28793d5..ca986e1 100644 --- a/deny.toml +++ b/deny.toml @@ -25,17 +25,11 @@ ignore = [ # validator_derive 0.20, the latest published version. No upstream fix yet. "RUSTSEC-2026-0173", # quick-xml DoS advisories (fix requires >=0.41); pulled in transitively by - # object_store 0.14 for its AWS/Azure XML list parsing. The latest released - # object_store still caps quick-xml at 0.40 — no upstream release to bump to - # yet. Remove once object_store ships a quick-xml >=0.41. + # object_store 0.14 for its AWS/Azure XML list parsing. The bump is merged + # upstream (apache/arrow-rs-object-store#785) but unreleased — v0.14.0 is + # still the latest crate. Remove once object_store ships it. "RUSTSEC-2026-0194", "RUSTSEC-2026-0195", - # Unmaintained crates pulled in transitively by the engine's codec stack - # (elide-codec): paste via its macros, ttf-parser via ab_glyph/imageproc - # text rendering. Both are advisory-only (not vulnerabilities) and vetted - # upstream in elide. Remove if elide drops them. - "RUSTSEC-2024-0436", - "RUSTSEC-2026-0192", ] [licenses] @@ -69,16 +63,8 @@ allow = [ "MPL-2.0", ] -# LGPL-3.0, accepted only for the engine's opt-in `mp3` codec feature. -# MP3 *encoding* has no permissively-licensed, production-quality alternative -# in the ecosystem — LAME (libmp3lame) is effectively the only option, and -# every substitute (incl. Shine) is also LGPL. The obligation is satisfiable -# for proprietary use via dynamic/separate linking. Mirrors the carve-out in -# the upstream elide repository. -exceptions = [ - { name = "mp3lame-encoder", allow = ["LGPL-3.0"] }, - { name = "mp3lame-sys", allow = ["LGPL-3.0"] }, -] +# For compound licenses, we'll be permissive and only block if they contain denied licenses +exceptions = [] [bans] # Lint level for when multiple versions of the same crate are detected From 99e9f7801adce01124b16973166ead387af23d5b Mon Sep 17 00:00:00 2001 From: Oleh Martsokha Date: Sun, 5 Jul 2026 14:49:12 +0200 Subject: [PATCH 7/7] chore: bump runtime/elide (Context reserved on analyze API) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pull the latest runtime: analyze_document now accepts a `contexts: &[Context]` argument (reserved API, consumption tracked in nvisycom/runtime#314), and the engine restructured its pipeline module (Document/AnalyzedDocument, anonymize_document). Our Context/Policy endpoints compile unchanged — the schema types are still re-exported via nvisy_schema::{context,policy}. Co-Authored-By: Claude Opus 4.8 (1M context) --- Cargo.lock | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad37f6e..734070d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1699,7 +1699,7 @@ dependencies = [ [[package]] name = "elide-bento" version = "0.1.0" -source = "git+https://github.com/nvisycom/runtime?branch=main#d993014f2a1bae6bb0c3738ffcdde59e3378528b" +source = "git+https://github.com/nvisycom/runtime?branch=main#a8655a33731f519a59c2aff01f5a06db53c829c0" dependencies = [ "async-trait", "base64", @@ -3671,10 +3671,25 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "nvisy-context" +version = "0.1.0" +source = "git+https://github.com/nvisycom/runtime?branch=main#a8655a33731f519a59c2aff01f5a06db53c829c0" +dependencies = [ + "derive_builder", + "derive_more", + "elide-core", + "jiff", + "schemars", + "semver", + "serde", + "uuid", +] + [[package]] name = "nvisy-core" version = "0.1.0" -source = "git+https://github.com/nvisycom/runtime?branch=main#d993014f2a1bae6bb0c3738ffcdde59e3378528b" +source = "git+https://github.com/nvisycom/runtime?branch=main#a8655a33731f519a59c2aff01f5a06db53c829c0" dependencies = [ "derive_builder", "derive_more", @@ -3689,7 +3704,7 @@ dependencies = [ [[package]] name = "nvisy-engine" version = "0.1.0" -source = "git+https://github.com/nvisycom/runtime?branch=main#d993014f2a1bae6bb0c3738ffcdde59e3378528b" +source = "git+https://github.com/nvisycom/runtime?branch=main#a8655a33731f519a59c2aff01f5a06db53c829c0" dependencies = [ "bytes", "elide", @@ -3745,6 +3760,21 @@ dependencies = [ "uuid", ] +[[package]] +name = "nvisy-policy" +version = "0.1.0" +source = "git+https://github.com/nvisycom/runtime?branch=main#a8655a33731f519a59c2aff01f5a06db53c829c0" +dependencies = [ + "derive_more", + "elide-core", + "hipstr", + "schemars", + "semver", + "serde", + "strum 0.28.0", + "uuid", +] + [[package]] name = "nvisy-postgres" version = "0.1.0" @@ -3778,19 +3808,17 @@ dependencies = [ [[package]] name = "nvisy-schema" version = "0.1.0" -source = "git+https://github.com/nvisycom/runtime?branch=main#d993014f2a1bae6bb0c3738ffcdde59e3378528b" +source = "git+https://github.com/nvisycom/runtime?branch=main#a8655a33731f519a59c2aff01f5a06db53c829c0" dependencies = [ "bytes", - "derive_builder", - "derive_more", "elide-core", "hipstr", "jiff", + "nvisy-context", + "nvisy-policy", "schemars", - "semver", "serde", "serde_json", - "strum 0.28.0", "uuid", ] @@ -4145,7 +4173,7 @@ dependencies = [ "nom 7.1.3", "once_cell", "postcard", - "quick-xml 0.41.0", + "quick-xml 0.40.1", "regex", "regex-cache", "serde",