[MIRROR ONLY] A correct and efficient ATProto blob proxy for secure content delivery. codeberg.org/Blooym/porxie
36
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: switch to xrpc, create admin xrpc auth extractor

Lyna 807ec66b 68db166e

+2249 -596
-1
.gitignore
··· 1 1 target/ 2 2 .direnv/ 3 - lexicons/ 4 3 5 4 test_server.py
+667 -282
Cargo.lock
··· 3 3 version = 4 4 4 5 5 [[package]] 6 - name = "abnf" 7 - version = "0.13.0" 8 - source = "registry+https://github.com/rust-lang/crates.io-index" 9 - checksum = "087113bd50d9adce24850eed5d0476c7d199d532fce8fab5173650331e09033a" 10 - dependencies = [ 11 - "abnf-core", 12 - "nom", 13 - ] 14 - 15 - [[package]] 16 - name = "abnf-core" 17 - version = "0.5.0" 18 - source = "registry+https://github.com/rust-lang/crates.io-index" 19 - checksum = "c44e09c43ae1c368fb91a03a566472d0087c26cf7e1b9e8e289c14ede681dd7d" 20 - dependencies = [ 21 - "nom", 22 - ] 23 - 24 - [[package]] 25 6 name = "adler2" 26 7 version = "2.0.1" 27 8 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 58 39 ] 59 40 60 41 [[package]] 42 + name = "allocator-api2" 43 + version = "0.2.21" 44 + source = "registry+https://github.com/rust-lang/crates.io-index" 45 + checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 46 + 47 + [[package]] 61 48 name = "android_system_properties" 62 49 version = "0.1.5" 63 50 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 186 173 dependencies = [ 187 174 "axum-core", 188 175 "bytes", 176 + "form_urlencoded", 189 177 "futures-util", 190 178 "http", 191 179 "http-body", ··· 201 189 "serde_core", 202 190 "serde_json", 203 191 "serde_path_to_error", 192 + "serde_urlencoded", 204 193 "sync_wrapper", 205 194 "tokio", 206 195 "tower", ··· 366 355 "proc-macro2", 367 356 "quote", 368 357 "rustversion", 369 - "syn 2.0.117", 358 + "syn", 370 359 ] 371 360 372 361 [[package]] 362 + name = "borrow-or-share" 363 + version = "0.2.4" 364 + source = "registry+https://github.com/rust-lang/crates.io-index" 365 + checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" 366 + 367 + [[package]] 373 368 name = "borsh" 374 369 version = "1.6.1" 375 370 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 401 396 ] 402 397 403 398 [[package]] 404 - name = "btree-range-map" 405 - version = "0.7.2" 406 - source = "registry+https://github.com/rust-lang/crates.io-index" 407 - checksum = "1be5c9672446d3800bcbcaabaeba121fe22f1fb25700c4562b22faf76d377c33" 408 - dependencies = [ 409 - "btree-slab", 410 - "cc-traits", 411 - "range-traits", 412 - "serde", 413 - "slab", 414 - ] 415 - 416 - [[package]] 417 - name = "btree-slab" 418 - version = "0.6.1" 419 - source = "registry+https://github.com/rust-lang/crates.io-index" 420 - checksum = "7a2b56d3029f075c4fa892428a098425b86cef5c89ae54073137ece416aef13c" 421 - dependencies = [ 422 - "cc-traits", 423 - "slab", 424 - "smallvec", 425 - ] 426 - 427 - [[package]] 428 399 name = "bumpalo" 429 400 version = "3.20.2" 430 401 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 473 444 ] 474 445 475 446 [[package]] 476 - name = "cc-traits" 477 - version = "2.0.0" 478 - source = "registry+https://github.com/rust-lang/crates.io-index" 479 - checksum = "060303ef31ef4a522737e1b1ab68c67916f2a787bb2f4f54f383279adba962b5" 480 - dependencies = [ 481 - "slab", 482 - ] 483 - 484 - [[package]] 485 447 name = "cfb" 486 448 version = "0.7.3" 487 449 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 589 551 "heck 0.5.0", 590 552 "proc-macro2", 591 553 "quote", 592 - "syn 2.0.117", 554 + "syn", 593 555 ] 594 556 595 557 [[package]] ··· 781 743 ] 782 744 783 745 [[package]] 746 + name = "curve25519-dalek" 747 + version = "4.1.3" 748 + source = "registry+https://github.com/rust-lang/crates.io-index" 749 + checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" 750 + dependencies = [ 751 + "cfg-if", 752 + "cpufeatures 0.2.17", 753 + "curve25519-dalek-derive", 754 + "digest 0.10.7", 755 + "fiat-crypto", 756 + "rustc_version", 757 + "subtle", 758 + "zeroize", 759 + ] 760 + 761 + [[package]] 762 + name = "curve25519-dalek-derive" 763 + version = "0.1.1" 764 + source = "registry+https://github.com/rust-lang/crates.io-index" 765 + checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" 766 + dependencies = [ 767 + "proc-macro2", 768 + "quote", 769 + "syn", 770 + ] 771 + 772 + [[package]] 784 773 name = "darling" 785 774 version = "0.23.0" 786 775 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 800 789 "proc-macro2", 801 790 "quote", 802 791 "strsim", 803 - "syn 2.0.117", 792 + "syn", 804 793 ] 805 794 806 795 [[package]] ··· 811 800 dependencies = [ 812 801 "darling_core", 813 802 "quote", 814 - "syn 2.0.117", 803 + "syn", 815 804 ] 816 805 817 806 [[package]] ··· 851 840 checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" 852 841 dependencies = [ 853 842 "data-encoding", 854 - "syn 2.0.117", 843 + "syn", 855 844 ] 856 845 857 846 [[package]] ··· 872 861 checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" 873 862 dependencies = [ 874 863 "powerfmt", 875 - "serde_core", 876 864 ] 877 865 878 866 [[package]] ··· 892 880 dependencies = [ 893 881 "proc-macro2", 894 882 "quote", 895 - "syn 2.0.117", 883 + "syn", 896 884 "unicode-xid", 897 885 ] 898 886 ··· 932 920 dependencies = [ 933 921 "proc-macro2", 934 922 "quote", 935 - "syn 2.0.117", 923 + "syn", 936 924 ] 937 925 938 926 [[package]] ··· 940 928 version = "0.15.7" 941 929 source = "registry+https://github.com/rust-lang/crates.io-index" 942 930 checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" 943 - 944 - [[package]] 945 - name = "dyn-clone" 946 - version = "1.0.20" 947 - source = "registry+https://github.com/rust-lang/crates.io-index" 948 - checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" 949 931 950 932 [[package]] 951 933 name = "ecdsa" ··· 962 944 ] 963 945 964 946 [[package]] 947 + name = "ed25519" 948 + version = "2.2.3" 949 + source = "registry+https://github.com/rust-lang/crates.io-index" 950 + checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" 951 + dependencies = [ 952 + "pkcs8", 953 + "signature", 954 + ] 955 + 956 + [[package]] 957 + name = "ed25519-dalek" 958 + version = "2.2.0" 959 + source = "registry+https://github.com/rust-lang/crates.io-index" 960 + checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" 961 + dependencies = [ 962 + "curve25519-dalek", 963 + "ed25519", 964 + "rand_core 0.6.4", 965 + "serde", 966 + "sha2 0.10.9", 967 + "subtle", 968 + "zeroize", 969 + ] 970 + 971 + [[package]] 965 972 name = "elliptic-curve" 966 973 version = "0.13.8" 967 974 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 973 980 "ff", 974 981 "generic-array", 975 982 "group", 983 + "hkdf", 976 984 "pem-rfc7468", 977 985 "pkcs8", 978 986 "rand_core 0.6.4", ··· 1056 1064 ] 1057 1065 1058 1066 [[package]] 1067 + name = "fiat-crypto" 1068 + version = "0.2.9" 1069 + source = "registry+https://github.com/rust-lang/crates.io-index" 1070 + checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" 1071 + 1072 + [[package]] 1059 1073 name = "find-msvc-tools" 1060 1074 version = "0.1.9" 1061 1075 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1072 1086 ] 1073 1087 1074 1088 [[package]] 1089 + name = "fluent-uri" 1090 + version = "0.4.1" 1091 + source = "registry+https://github.com/rust-lang/crates.io-index" 1092 + checksum = "bc74ac4d8359ae70623506d512209619e5cf8f347124910440dbc221714b328e" 1093 + dependencies = [ 1094 + "borrow-or-share", 1095 + "ref-cast", 1096 + "serde", 1097 + ] 1098 + 1099 + [[package]] 1075 1100 name = "fnv" 1076 1101 version = "1.0.7" 1077 1102 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1093 1118 ] 1094 1119 1095 1120 [[package]] 1121 + name = "futf" 1122 + version = "0.1.5" 1123 + source = "registry+https://github.com/rust-lang/crates.io-index" 1124 + checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" 1125 + dependencies = [ 1126 + "mac", 1127 + "new_debug_unreachable", 1128 + ] 1129 + 1130 + [[package]] 1096 1131 name = "futures-buffered" 1097 1132 version = "0.2.13" 1098 1133 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1147 1182 dependencies = [ 1148 1183 "proc-macro2", 1149 1184 "quote", 1150 - "syn 2.0.117", 1185 + "syn", 1151 1186 ] 1152 1187 1153 1188 [[package]] ··· 1245 1280 ] 1246 1281 1247 1282 [[package]] 1283 + name = "gloo-storage" 1284 + version = "0.3.0" 1285 + source = "registry+https://github.com/rust-lang/crates.io-index" 1286 + checksum = "fbc8031e8c92758af912f9bc08fbbadd3c6f3cfcbf6b64cdf3d6a81f0139277a" 1287 + dependencies = [ 1288 + "gloo-utils", 1289 + "js-sys", 1290 + "serde", 1291 + "serde_json", 1292 + "thiserror 1.0.69", 1293 + "wasm-bindgen", 1294 + "web-sys", 1295 + ] 1296 + 1297 + [[package]] 1298 + name = "gloo-utils" 1299 + version = "0.2.0" 1300 + source = "registry+https://github.com/rust-lang/crates.io-index" 1301 + checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" 1302 + dependencies = [ 1303 + "js-sys", 1304 + "serde", 1305 + "serde_json", 1306 + "wasm-bindgen", 1307 + "web-sys", 1308 + ] 1309 + 1310 + [[package]] 1248 1311 name = "group" 1249 1312 version = "0.13.0" 1250 1313 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1267 1330 "futures-core", 1268 1331 "futures-sink", 1269 1332 "http", 1270 - "indexmap 2.14.0", 1333 + "indexmap", 1271 1334 "slab", 1272 1335 "tokio", 1273 1336 "tokio-util", ··· 1293 1356 dependencies = [ 1294 1357 "byteorder", 1295 1358 ] 1296 - 1297 - [[package]] 1298 - name = "hashbrown" 1299 - version = "0.12.3" 1300 - source = "registry+https://github.com/rust-lang/crates.io-index" 1301 - checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 1302 1359 1303 1360 [[package]] 1304 1361 name = "hashbrown" ··· 1312 1369 source = "registry+https://github.com/rust-lang/crates.io-index" 1313 1370 checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 1314 1371 dependencies = [ 1372 + "allocator-api2", 1373 + "equivalent", 1315 1374 "foldhash", 1316 1375 ] 1317 1376 ··· 1378 1437 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 1379 1438 1380 1439 [[package]] 1381 - name = "hex_fmt" 1382 - version = "0.3.0" 1440 + name = "hkdf" 1441 + version = "0.12.4" 1383 1442 source = "registry+https://github.com/rust-lang/crates.io-index" 1384 - checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" 1443 + checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" 1444 + dependencies = [ 1445 + "hmac", 1446 + ] 1385 1447 1386 1448 [[package]] 1387 1449 name = "hmac" ··· 1390 1452 checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 1391 1453 dependencies = [ 1392 1454 "digest 0.10.7", 1455 + ] 1456 + 1457 + [[package]] 1458 + name = "html5ever" 1459 + version = "0.27.0" 1460 + source = "registry+https://github.com/rust-lang/crates.io-index" 1461 + checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" 1462 + dependencies = [ 1463 + "log", 1464 + "mac", 1465 + "markup5ever", 1466 + "proc-macro2", 1467 + "quote", 1468 + "syn", 1393 1469 ] 1394 1470 1395 1471 [[package]] ··· 1656 1732 1657 1733 [[package]] 1658 1734 name = "indexmap" 1659 - version = "1.9.3" 1660 - source = "registry+https://github.com/rust-lang/crates.io-index" 1661 - checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 1662 - dependencies = [ 1663 - "autocfg", 1664 - "hashbrown 0.12.3", 1665 - "serde", 1666 - ] 1667 - 1668 - [[package]] 1669 - name = "indexmap" 1670 1735 version = "2.14.0" 1671 1736 source = "registry+https://github.com/rust-lang/crates.io-index" 1672 1737 checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" ··· 1675 1740 "hashbrown 0.17.0", 1676 1741 "serde", 1677 1742 "serde_core", 1678 - ] 1679 - 1680 - [[package]] 1681 - name = "indoc" 1682 - version = "2.0.7" 1683 - source = "registry+https://github.com/rust-lang/crates.io-index" 1684 - checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" 1685 - dependencies = [ 1686 - "rustversion", 1687 1743 ] 1688 1744 1689 1745 [[package]] ··· 1744 1800 checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" 1745 1801 1746 1802 [[package]] 1803 + name = "jacquard" 1804 + version = "0.11.0" 1805 + source = "registry+https://github.com/rust-lang/crates.io-index" 1806 + checksum = "1e4bb22329646c06eb07eb63ae6d33320e8f52ae8b5a5444257f27c3d68d44ae" 1807 + dependencies = [ 1808 + "bytes", 1809 + "getrandom 0.2.17", 1810 + "gloo-storage", 1811 + "http", 1812 + "jacquard-api", 1813 + "jacquard-common", 1814 + "jacquard-identity", 1815 + "jacquard-oauth", 1816 + "jose-jwk", 1817 + "miette", 1818 + "regex", 1819 + "regex-lite", 1820 + "reqwest", 1821 + "serde", 1822 + "serde_html_form", 1823 + "serde_json", 1824 + "smol_str", 1825 + "thiserror 2.0.18", 1826 + "tokio", 1827 + "trait-variant", 1828 + "webpage", 1829 + ] 1830 + 1831 + [[package]] 1747 1832 name = "jacquard-api" 1748 - version = "0.9.5" 1833 + version = "0.11.1" 1749 1834 source = "registry+https://github.com/rust-lang/crates.io-index" 1750 - checksum = "4979fb1848c1dd7ac8fd12745bc71f56f6da61374407d5f9b06005467a954e5a" 1835 + checksum = "f4bba022e9c632f737de481d7c894b5736dd1a503362876ca4da49b9255a7e61" 1751 1836 dependencies = [ 1752 - "bon", 1837 + "jacquard-common", 1838 + "jacquard-derive", 1839 + "jacquard-lexicon", 1840 + "miette", 1841 + "serde", 1842 + "thiserror 2.0.18", 1843 + ] 1844 + 1845 + [[package]] 1846 + name = "jacquard-axum" 1847 + version = "0.11.0" 1848 + source = "registry+https://github.com/rust-lang/crates.io-index" 1849 + checksum = "707649f378c7aaac47a267e1eb95dbcf595fca3b0c949e0e7aa742dec7093cde" 1850 + dependencies = [ 1851 + "axum", 1753 1852 "bytes", 1853 + "jacquard", 1754 1854 "jacquard-common", 1755 1855 "jacquard-derive", 1756 - "jacquard-lexicon", 1757 1856 "miette", 1758 - "rustversion", 1759 1857 "serde", 1760 - "serde_bytes", 1761 - "serde_ipld_dagcbor", 1858 + "serde_html_form", 1859 + "serde_json", 1762 1860 "thiserror 2.0.18", 1763 - "unicode-segmentation", 1861 + "tokio", 1862 + "tower-http", 1863 + "tracing", 1764 1864 ] 1765 1865 1766 1866 [[package]] 1767 1867 name = "jacquard-common" 1768 - version = "0.9.5" 1868 + version = "0.11.0" 1769 1869 source = "registry+https://github.com/rust-lang/crates.io-index" 1770 - checksum = "1751921e0bdae5e0077afade6161545e9ef7698306c868f800916e99ecbcaae9" 1870 + checksum = "9631f08f1e65d19e204bc6774d00b4e0b69fb649d7d7ac59ccf97797bd9a196e" 1771 1871 dependencies = [ 1772 1872 "base64", 1773 1873 "bon", 1774 1874 "bytes", 1775 1875 "chrono", 1876 + "ciborium", 1877 + "ciborium-io", 1776 1878 "cid", 1879 + "fluent-uri", 1777 1880 "getrandom 0.2.17", 1778 1881 "getrandom 0.3.4", 1882 + "hashbrown 0.15.5", 1779 1883 "http", 1780 1884 "ipld-core", 1781 1885 "k256", 1782 - "langtag", 1886 + "maitake-sync", 1783 1887 "miette", 1784 1888 "multibase", 1785 1889 "multihash", 1786 1890 "ouroboros", 1891 + "oxilangtag", 1787 1892 "p256", 1893 + "phf", 1788 1894 "postcard", 1789 - "rand", 1895 + "rand 0.9.4", 1790 1896 "regex", 1897 + "regex-automata", 1791 1898 "regex-lite", 1792 1899 "reqwest", 1900 + "rustversion", 1793 1901 "serde", 1794 1902 "serde_bytes", 1795 1903 "serde_html_form", ··· 1797 1905 "serde_json", 1798 1906 "signature", 1799 1907 "smol_str", 1908 + "spin 0.10.0", 1800 1909 "thiserror 2.0.18", 1801 1910 "tokio", 1802 1911 "tokio-util", 1803 1912 "trait-variant", 1804 - "url", 1913 + "unicode-segmentation", 1805 1914 ] 1806 1915 1807 1916 [[package]] 1808 1917 name = "jacquard-derive" 1809 - version = "0.9.5" 1918 + version = "0.11.0" 1810 1919 source = "registry+https://github.com/rust-lang/crates.io-index" 1811 - checksum = "9c8d73dfee07943fdab93569ed1c28b06c6921ed891c08b415c4a323ff67e593" 1920 + checksum = "22904bd0f9a959591e14ee9e1dab91a9110209cb8b292930e584134f96a83ece" 1812 1921 dependencies = [ 1813 1922 "heck 0.5.0", 1814 1923 "jacquard-lexicon", 1815 1924 "proc-macro2", 1816 1925 "quote", 1817 - "syn 2.0.117", 1926 + "syn", 1818 1927 ] 1819 1928 1820 1929 [[package]] 1821 1930 name = "jacquard-identity" 1822 - version = "0.9.5" 1931 + version = "0.11.0" 1823 1932 source = "registry+https://github.com/rust-lang/crates.io-index" 1824 - checksum = "e7aaefa819fa4213cf59f180dba932f018a7cd0599582fd38474ee2a38c16cf2" 1933 + checksum = "b84a9302ea9dd39c49d748a8eba21faf365570088571ff327ebb00117dbd65b8" 1825 1934 dependencies = [ 1826 1935 "bon", 1827 1936 "bytes", 1828 1937 "http", 1829 - "jacquard-api", 1830 1938 "jacquard-common", 1831 1939 "jacquard-lexicon", 1832 1940 "miette", 1941 + "mini-moka-wasm", 1833 1942 "n0-future", 1834 - "percent-encoding", 1835 1943 "reqwest", 1836 1944 "serde", 1837 1945 "serde_html_form", ··· 1840 1948 "tokio", 1841 1949 "tracing", 1842 1950 "trait-variant", 1843 - "url", 1844 - "urlencoding", 1845 1951 ] 1846 1952 1847 1953 [[package]] 1848 1954 name = "jacquard-lexicon" 1849 - version = "0.9.5" 1955 + version = "0.11.1" 1850 1956 source = "registry+https://github.com/rust-lang/crates.io-index" 1851 - checksum = "8411aff546569b0a1e0ef669bed2380cec1c00d48f02f3fcd57a71545321b3d8" 1957 + checksum = "fd7863d4f56a49f07391b5f775e82be12e6381156642ee83574f481ca73e8b0e" 1852 1958 dependencies = [ 1853 1959 "cid", 1854 1960 "dashmap", ··· 1863 1969 "serde", 1864 1970 "serde_ipld_dagcbor", 1865 1971 "serde_json", 1972 + "serde_path_to_error", 1866 1973 "serde_repr", 1867 1974 "serde_with", 1868 1975 "sha2 0.10.9", 1869 - "syn 2.0.117", 1976 + "syn", 1870 1977 "thiserror 2.0.18", 1871 1978 "unicode-segmentation", 1872 1979 ] 1873 1980 1874 1981 [[package]] 1982 + name = "jacquard-oauth" 1983 + version = "0.11.0" 1984 + source = "registry+https://github.com/rust-lang/crates.io-index" 1985 + checksum = "ca0b3a8b765b3d3f1890233f9f62dda1b1524d1dc9967c310e5b009b93443776" 1986 + dependencies = [ 1987 + "base64", 1988 + "bytes", 1989 + "chrono", 1990 + "dashmap", 1991 + "ed25519-dalek", 1992 + "elliptic-curve", 1993 + "http", 1994 + "jacquard-common", 1995 + "jacquard-identity", 1996 + "jose-jwa", 1997 + "jose-jwk", 1998 + "k256", 1999 + "miette", 2000 + "p256", 2001 + "p384", 2002 + "rand 0.8.6", 2003 + "serde", 2004 + "serde_html_form", 2005 + "serde_json", 2006 + "sha2 0.10.9", 2007 + "smol_str", 2008 + "thiserror 2.0.18", 2009 + "tokio", 2010 + "trait-variant", 2011 + ] 2012 + 2013 + [[package]] 1875 2014 name = "jemalloc-sys" 1876 2015 version = "0.5.4+5.3.0-patched" 1877 2016 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1902 2041 ] 1903 2042 1904 2043 [[package]] 2044 + name = "jose-b64" 2045 + version = "0.1.2" 2046 + source = "registry+https://github.com/rust-lang/crates.io-index" 2047 + checksum = "bec69375368709666b21c76965ce67549f2d2db7605f1f8707d17c9656801b56" 2048 + dependencies = [ 2049 + "base64ct", 2050 + "serde", 2051 + "subtle", 2052 + "zeroize", 2053 + ] 2054 + 2055 + [[package]] 2056 + name = "jose-jwa" 2057 + version = "0.1.2" 2058 + source = "registry+https://github.com/rust-lang/crates.io-index" 2059 + checksum = "9ab78e053fe886a351d67cf0d194c000f9d0dcb92906eb34d853d7e758a4b3a7" 2060 + dependencies = [ 2061 + "serde", 2062 + ] 2063 + 2064 + [[package]] 2065 + name = "jose-jwk" 2066 + version = "0.1.2" 2067 + source = "registry+https://github.com/rust-lang/crates.io-index" 2068 + checksum = "280fa263807fe0782ecb6f2baadc28dffc04e00558a58e33bfdb801d11fd58e7" 2069 + dependencies = [ 2070 + "jose-b64", 2071 + "jose-jwa", 2072 + "p256", 2073 + "p384", 2074 + "rsa", 2075 + "serde", 2076 + "zeroize", 2077 + ] 2078 + 2079 + [[package]] 1905 2080 name = "js-sys" 1906 2081 version = "0.3.97" 1907 2082 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1938 2113 "cfg-if", 1939 2114 "ecdsa", 1940 2115 "elliptic-curve", 2116 + "once_cell", 1941 2117 "sha2 0.10.9", 2118 + "signature", 1942 2119 ] 1943 2120 1944 2121 [[package]] ··· 1952 2129 ] 1953 2130 1954 2131 [[package]] 1955 - name = "langtag" 1956 - version = "0.4.0" 1957 - source = "registry+https://github.com/rust-lang/crates.io-index" 1958 - checksum = "9ecb4c689a30e48ebeaa14237f34037e300dd072e6ad21a9ec72e810ff3c6600" 1959 - dependencies = [ 1960 - "serde", 1961 - "static-regular-grammar", 1962 - "thiserror 1.0.69", 1963 - ] 1964 - 1965 - [[package]] 1966 2132 name = "lazy_static" 1967 2133 version = "1.5.0" 1968 2134 source = "registry+https://github.com/rust-lang/crates.io-index" 1969 2135 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 2136 + dependencies = [ 2137 + "spin 0.9.8", 2138 + ] 1970 2139 1971 2140 [[package]] 1972 2141 name = "leb128fmt" ··· 1975 2144 checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" 1976 2145 1977 2146 [[package]] 2147 + name = "lexgen" 2148 + version = "0.0.0" 2149 + dependencies = [ 2150 + "jacquard-common", 2151 + "jacquard-derive", 2152 + "jacquard-lexicon", 2153 + "miette", 2154 + "serde", 2155 + "thiserror 2.0.18", 2156 + ] 2157 + 2158 + [[package]] 1978 2159 name = "libc" 1979 2160 version = "0.2.186" 1980 2161 source = "registry+https://github.com/rust-lang/crates.io-index" 1981 2162 checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" 2163 + 2164 + [[package]] 2165 + name = "libm" 2166 + version = "0.2.16" 2167 + source = "registry+https://github.com/rust-lang/crates.io-index" 2168 + checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" 1982 2169 1983 2170 [[package]] 1984 2171 name = "litemap" ··· 2021 2208 checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" 2022 2209 2023 2210 [[package]] 2211 + name = "mac" 2212 + version = "0.1.1" 2213 + source = "registry+https://github.com/rust-lang/crates.io-index" 2214 + checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" 2215 + 2216 + [[package]] 2217 + name = "maitake-sync" 2218 + version = "0.1.2" 2219 + source = "registry+https://github.com/rust-lang/crates.io-index" 2220 + checksum = "6816ab14147f80234c675b80ed6dc4f440d8a1cefc158e766067aedb84c0bcd5" 2221 + dependencies = [ 2222 + "cordyceps", 2223 + "loom", 2224 + "mycelium-bitfield", 2225 + "pin-project", 2226 + "portable-atomic", 2227 + ] 2228 + 2229 + [[package]] 2230 + name = "markup5ever" 2231 + version = "0.12.1" 2232 + source = "registry+https://github.com/rust-lang/crates.io-index" 2233 + checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" 2234 + dependencies = [ 2235 + "log", 2236 + "phf", 2237 + "phf_codegen", 2238 + "string_cache", 2239 + "string_cache_codegen", 2240 + "tendril", 2241 + ] 2242 + 2243 + [[package]] 2244 + name = "markup5ever_rcdom" 2245 + version = "0.3.0" 2246 + source = "registry+https://github.com/rust-lang/crates.io-index" 2247 + checksum = "edaa21ab3701bfee5099ade5f7e1f84553fd19228cf332f13cd6e964bf59be18" 2248 + dependencies = [ 2249 + "html5ever", 2250 + "markup5ever", 2251 + "tendril", 2252 + "xml5ever", 2253 + ] 2254 + 2255 + [[package]] 2024 2256 name = "match-lookup" 2025 2257 version = "0.1.2" 2026 2258 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2028 2260 dependencies = [ 2029 2261 "proc-macro2", 2030 2262 "quote", 2031 - "syn 2.0.117", 2263 + "syn", 2032 2264 ] 2033 2265 2034 2266 [[package]] ··· 2071 2303 dependencies = [ 2072 2304 "proc-macro2", 2073 2305 "quote", 2074 - "syn 2.0.117", 2306 + "syn", 2075 2307 ] 2076 2308 2077 2309 [[package]] ··· 2081 2313 checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 2082 2314 2083 2315 [[package]] 2084 - name = "minimal-lexical" 2085 - version = "0.2.1" 2316 + name = "mini-moka-wasm" 2317 + version = "0.10.99" 2086 2318 source = "registry+https://github.com/rust-lang/crates.io-index" 2087 - checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 2319 + checksum = "0102b9a2ad50fa47ca89eead2316c8222285ecfbd3f69ce99564fbe4253866e8" 2320 + dependencies = [ 2321 + "crossbeam-channel", 2322 + "crossbeam-utils", 2323 + "dashmap", 2324 + "smallvec", 2325 + "tagptr", 2326 + "triomphe", 2327 + "web-time", 2328 + ] 2088 2329 2089 2330 [[package]] 2090 2331 name = "miniz_oxide" ··· 2186 2427 "proc-macro-crate", 2187 2428 "proc-macro2", 2188 2429 "quote", 2189 - "syn 2.0.117", 2430 + "syn", 2190 2431 "synstructure", 2191 2432 ] 2433 + 2434 + [[package]] 2435 + name = "mycelium-bitfield" 2436 + version = "0.1.5" 2437 + source = "registry+https://github.com/rust-lang/crates.io-index" 2438 + checksum = "24e0cc5e2c585acbd15c5ce911dff71e1f4d5313f43345873311c4f5efd741cc" 2192 2439 2193 2440 [[package]] 2194 2441 name = "n0-future" ··· 2212 2459 ] 2213 2460 2214 2461 [[package]] 2215 - name = "nom" 2216 - version = "7.1.3" 2462 + name = "new_debug_unreachable" 2463 + version = "1.0.6" 2217 2464 source = "registry+https://github.com/rust-lang/crates.io-index" 2218 - checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 2219 - dependencies = [ 2220 - "memchr", 2221 - "minimal-lexical", 2222 - ] 2465 + checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" 2223 2466 2224 2467 [[package]] 2225 2468 name = "ntapi" ··· 2240 2483 ] 2241 2484 2242 2485 [[package]] 2486 + name = "num-bigint-dig" 2487 + version = "0.8.6" 2488 + source = "registry+https://github.com/rust-lang/crates.io-index" 2489 + checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" 2490 + dependencies = [ 2491 + "lazy_static", 2492 + "libm", 2493 + "num-integer", 2494 + "num-iter", 2495 + "num-traits", 2496 + "rand 0.8.6", 2497 + "smallvec", 2498 + "zeroize", 2499 + ] 2500 + 2501 + [[package]] 2243 2502 name = "num-conv" 2244 2503 version = "0.2.1" 2245 2504 source = "registry+https://github.com/rust-lang/crates.io-index" 2246 2505 checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" 2247 2506 2248 2507 [[package]] 2508 + name = "num-integer" 2509 + version = "0.1.46" 2510 + source = "registry+https://github.com/rust-lang/crates.io-index" 2511 + checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 2512 + dependencies = [ 2513 + "num-traits", 2514 + ] 2515 + 2516 + [[package]] 2517 + name = "num-iter" 2518 + version = "0.1.45" 2519 + source = "registry+https://github.com/rust-lang/crates.io-index" 2520 + checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" 2521 + dependencies = [ 2522 + "autocfg", 2523 + "num-integer", 2524 + "num-traits", 2525 + ] 2526 + 2527 + [[package]] 2249 2528 name = "num-traits" 2250 2529 version = "0.2.19" 2251 2530 source = "registry+https://github.com/rust-lang/crates.io-index" 2252 2531 checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 2253 2532 dependencies = [ 2254 2533 "autocfg", 2534 + "libm", 2255 2535 ] 2256 2536 2257 2537 [[package]] ··· 2306 2586 "proc-macro2", 2307 2587 "proc-macro2-diagnostics", 2308 2588 "quote", 2309 - "syn 2.0.117", 2589 + "syn", 2590 + ] 2591 + 2592 + [[package]] 2593 + name = "oxilangtag" 2594 + version = "0.1.5" 2595 + source = "registry+https://github.com/rust-lang/crates.io-index" 2596 + checksum = "23f3f87617a86af77fa3691e6350483e7154c2ead9f1261b75130e21ca0f8acb" 2597 + dependencies = [ 2598 + "serde", 2310 2599 ] 2311 2600 2312 2601 [[package]] ··· 2322 2611 ] 2323 2612 2324 2613 [[package]] 2614 + name = "p384" 2615 + version = "0.13.1" 2616 + source = "registry+https://github.com/rust-lang/crates.io-index" 2617 + checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" 2618 + dependencies = [ 2619 + "ecdsa", 2620 + "elliptic-curve", 2621 + "primeorder", 2622 + "sha2 0.10.9", 2623 + ] 2624 + 2625 + [[package]] 2325 2626 name = "parking" 2326 2627 version = "2.2.1" 2327 2628 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2366 2667 checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 2367 2668 2368 2669 [[package]] 2670 + name = "phf" 2671 + version = "0.11.3" 2672 + source = "registry+https://github.com/rust-lang/crates.io-index" 2673 + checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" 2674 + dependencies = [ 2675 + "phf_macros", 2676 + "phf_shared", 2677 + ] 2678 + 2679 + [[package]] 2680 + name = "phf_codegen" 2681 + version = "0.11.3" 2682 + source = "registry+https://github.com/rust-lang/crates.io-index" 2683 + checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" 2684 + dependencies = [ 2685 + "phf_generator", 2686 + "phf_shared", 2687 + ] 2688 + 2689 + [[package]] 2690 + name = "phf_generator" 2691 + version = "0.11.3" 2692 + source = "registry+https://github.com/rust-lang/crates.io-index" 2693 + checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" 2694 + dependencies = [ 2695 + "phf_shared", 2696 + "rand 0.8.6", 2697 + ] 2698 + 2699 + [[package]] 2700 + name = "phf_macros" 2701 + version = "0.11.3" 2702 + source = "registry+https://github.com/rust-lang/crates.io-index" 2703 + checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" 2704 + dependencies = [ 2705 + "phf_generator", 2706 + "phf_shared", 2707 + "proc-macro2", 2708 + "quote", 2709 + "syn", 2710 + ] 2711 + 2712 + [[package]] 2713 + name = "phf_shared" 2714 + version = "0.11.3" 2715 + source = "registry+https://github.com/rust-lang/crates.io-index" 2716 + checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" 2717 + dependencies = [ 2718 + "siphasher", 2719 + ] 2720 + 2721 + [[package]] 2369 2722 name = "pin-project" 2370 2723 version = "1.1.11" 2371 2724 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2382 2735 dependencies = [ 2383 2736 "proc-macro2", 2384 2737 "quote", 2385 - "syn 2.0.117", 2738 + "syn", 2386 2739 ] 2387 2740 2388 2741 [[package]] ··· 2392 2745 checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" 2393 2746 2394 2747 [[package]] 2748 + name = "pkcs1" 2749 + version = "0.7.5" 2750 + source = "registry+https://github.com/rust-lang/crates.io-index" 2751 + checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" 2752 + dependencies = [ 2753 + "der", 2754 + "pkcs8", 2755 + "spki", 2756 + ] 2757 + 2758 + [[package]] 2395 2759 name = "pkcs8" 2396 2760 version = "0.10.2" 2397 2761 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2428 2792 "futures-util", 2429 2793 "humantime", 2430 2794 "infer", 2795 + "jacquard-axum", 2431 2796 "jacquard-common", 2432 2797 "jacquard-identity", 2433 2798 "jemallocator", 2434 2799 "json-subscriber", 2800 + "lexgen", 2435 2801 "mime", 2436 2802 "moka", 2437 2803 "multihash-codetable", ··· 2482 2848 dependencies = [ 2483 2849 "zerocopy", 2484 2850 ] 2851 + 2852 + [[package]] 2853 + name = "precomputed-hash" 2854 + version = "0.1.1" 2855 + source = "registry+https://github.com/rust-lang/crates.io-index" 2856 + checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 2485 2857 2486 2858 [[package]] 2487 2859 name = "prettyplease" ··· 2490 2862 checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 2491 2863 dependencies = [ 2492 2864 "proc-macro2", 2493 - "syn 2.0.117", 2865 + "syn", 2494 2866 ] 2495 2867 2496 2868 [[package]] ··· 2512 2884 ] 2513 2885 2514 2886 [[package]] 2515 - name = "proc-macro-error" 2516 - version = "1.0.4" 2517 - source = "registry+https://github.com/rust-lang/crates.io-index" 2518 - checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 2519 - dependencies = [ 2520 - "proc-macro-error-attr", 2521 - "proc-macro2", 2522 - "quote", 2523 - "syn 1.0.109", 2524 - "version_check", 2525 - ] 2526 - 2527 - [[package]] 2528 - name = "proc-macro-error-attr" 2529 - version = "1.0.4" 2530 - source = "registry+https://github.com/rust-lang/crates.io-index" 2531 - checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 2532 - dependencies = [ 2533 - "proc-macro2", 2534 - "quote", 2535 - "version_check", 2536 - ] 2537 - 2538 - [[package]] 2539 2887 name = "proc-macro2" 2540 2888 version = "1.0.106" 2541 2889 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2552 2900 dependencies = [ 2553 2901 "proc-macro2", 2554 2902 "quote", 2555 - "syn 2.0.117", 2903 + "syn", 2556 2904 "version_check", 2557 2905 "yansi", 2558 2906 ] ··· 2586 2934 "bytes", 2587 2935 "getrandom 0.3.4", 2588 2936 "lru-slab", 2589 - "rand", 2937 + "rand 0.9.4", 2590 2938 "ring", 2591 2939 "rustc-hash", 2592 2940 "rustls", ··· 2635 2983 2636 2984 [[package]] 2637 2985 name = "rand" 2986 + version = "0.8.6" 2987 + source = "registry+https://github.com/rust-lang/crates.io-index" 2988 + checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" 2989 + dependencies = [ 2990 + "libc", 2991 + "rand_chacha 0.3.1", 2992 + "rand_core 0.6.4", 2993 + ] 2994 + 2995 + [[package]] 2996 + name = "rand" 2638 2997 version = "0.9.4" 2639 2998 source = "registry+https://github.com/rust-lang/crates.io-index" 2640 2999 checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" 2641 3000 dependencies = [ 2642 - "rand_chacha", 3001 + "rand_chacha 0.9.0", 2643 3002 "rand_core 0.9.5", 2644 3003 ] 2645 3004 2646 3005 [[package]] 2647 3006 name = "rand_chacha" 3007 + version = "0.3.1" 3008 + source = "registry+https://github.com/rust-lang/crates.io-index" 3009 + checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 3010 + dependencies = [ 3011 + "ppv-lite86", 3012 + "rand_core 0.6.4", 3013 + ] 3014 + 3015 + [[package]] 3016 + name = "rand_chacha" 2648 3017 version = "0.9.0" 2649 3018 source = "registry+https://github.com/rust-lang/crates.io-index" 2650 3019 checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" ··· 2672 3041 ] 2673 3042 2674 3043 [[package]] 2675 - name = "range-traits" 2676 - version = "0.3.2" 2677 - source = "registry+https://github.com/rust-lang/crates.io-index" 2678 - checksum = "d20581732dd76fa913c7dff1a2412b714afe3573e94d41c34719de73337cc8ab" 2679 - 2680 - [[package]] 2681 3044 name = "redox_syscall" 2682 3045 version = "0.5.18" 2683 3046 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2703 3066 dependencies = [ 2704 3067 "proc-macro2", 2705 3068 "quote", 2706 - "syn 2.0.117", 3069 + "syn", 2707 3070 ] 2708 3071 2709 3072 [[package]] ··· 2819 3182 ] 2820 3183 2821 3184 [[package]] 3185 + name = "rsa" 3186 + version = "0.9.10" 3187 + source = "registry+https://github.com/rust-lang/crates.io-index" 3188 + checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" 3189 + dependencies = [ 3190 + "const-oid", 3191 + "digest 0.10.7", 3192 + "num-bigint-dig", 3193 + "num-integer", 3194 + "num-traits", 3195 + "pkcs1", 3196 + "pkcs8", 3197 + "rand_core 0.6.4", 3198 + "signature", 3199 + "spki", 3200 + "subtle", 3201 + "zeroize", 3202 + ] 3203 + 3204 + [[package]] 2822 3205 name = "rustc-hash" 2823 3206 version = "2.1.2" 2824 3207 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2881 3264 checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" 2882 3265 2883 3266 [[package]] 2884 - name = "schemars" 2885 - version = "0.9.0" 2886 - source = "registry+https://github.com/rust-lang/crates.io-index" 2887 - checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" 2888 - dependencies = [ 2889 - "dyn-clone", 2890 - "ref-cast", 2891 - "serde", 2892 - "serde_json", 2893 - ] 2894 - 2895 - [[package]] 2896 - name = "schemars" 2897 - version = "1.2.1" 2898 - source = "registry+https://github.com/rust-lang/crates.io-index" 2899 - checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" 2900 - dependencies = [ 2901 - "dyn-clone", 2902 - "ref-cast", 2903 - "serde", 2904 - "serde_json", 2905 - ] 2906 - 2907 - [[package]] 2908 3267 name = "scoped-tls" 2909 3268 version = "1.0.1" 2910 3269 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2979 3338 dependencies = [ 2980 3339 "proc-macro2", 2981 3340 "quote", 2982 - "syn 2.0.117", 3341 + "syn", 2983 3342 ] 2984 3343 2985 3344 [[package]] 2986 3345 name = "serde_html_form" 2987 - version = "0.2.8" 3346 + version = "0.3.2" 2988 3347 source = "registry+https://github.com/rust-lang/crates.io-index" 2989 - checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" 3348 + checksum = "2acf96b1d9364968fce46ebb548f1c0e1d7eceae27bdff73865d42e6c7369d94" 2990 3349 dependencies = [ 2991 3350 "form_urlencoded", 2992 - "indexmap 2.14.0", 3351 + "indexmap", 2993 3352 "itoa", 2994 - "ryu", 2995 3353 "serde_core", 2996 3354 ] 2997 3355 ··· 3039 3397 dependencies = [ 3040 3398 "proc-macro2", 3041 3399 "quote", 3042 - "syn 2.0.117", 3400 + "syn", 3043 3401 ] 3044 3402 3045 3403 [[package]] ··· 3063 3421 "base64", 3064 3422 "chrono", 3065 3423 "hex", 3066 - "indexmap 1.9.3", 3067 - "indexmap 2.14.0", 3068 - "schemars 0.9.0", 3069 - "schemars 1.2.1", 3070 3424 "serde_core", 3071 3425 "serde_json", 3072 3426 "serde_with_macros", ··· 3082 3436 "darling", 3083 3437 "proc-macro2", 3084 3438 "quote", 3085 - "syn 2.0.117", 3439 + "syn", 3086 3440 ] 3087 3441 3088 3442 [[package]] ··· 3181 3535 checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" 3182 3536 3183 3537 [[package]] 3538 + name = "siphasher" 3539 + version = "1.0.2" 3540 + source = "registry+https://github.com/rust-lang/crates.io-index" 3541 + checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" 3542 + 3543 + [[package]] 3184 3544 name = "slab" 3185 3545 version = "0.4.12" 3186 3546 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3244 3604 checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 3245 3605 3246 3606 [[package]] 3247 - name = "static-regular-grammar" 3248 - version = "2.0.2" 3607 + name = "static_assertions" 3608 + version = "1.1.0" 3609 + source = "registry+https://github.com/rust-lang/crates.io-index" 3610 + checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 3611 + 3612 + [[package]] 3613 + name = "string_cache" 3614 + version = "0.8.9" 3249 3615 source = "registry+https://github.com/rust-lang/crates.io-index" 3250 - checksum = "4f4a6c40247579acfbb138c3cd7de3dab113ab4ac6227f1b7de7d626ee667957" 3616 + checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" 3251 3617 dependencies = [ 3252 - "abnf", 3253 - "btree-range-map", 3254 - "ciborium", 3255 - "hex_fmt", 3256 - "indoc", 3257 - "proc-macro-error", 3258 - "proc-macro2", 3259 - "quote", 3618 + "new_debug_unreachable", 3619 + "parking_lot", 3620 + "phf_shared", 3621 + "precomputed-hash", 3260 3622 "serde", 3261 - "sha2 0.10.9", 3262 - "syn 2.0.117", 3263 - "thiserror 1.0.69", 3264 3623 ] 3265 3624 3266 3625 [[package]] 3267 - name = "static_assertions" 3268 - version = "1.1.0" 3626 + name = "string_cache_codegen" 3627 + version = "0.5.4" 3269 3628 source = "registry+https://github.com/rust-lang/crates.io-index" 3270 - checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 3629 + checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" 3630 + dependencies = [ 3631 + "phf_generator", 3632 + "phf_shared", 3633 + "proc-macro2", 3634 + "quote", 3635 + ] 3271 3636 3272 3637 [[package]] 3273 3638 name = "strsim" ··· 3280 3645 version = "2.6.1" 3281 3646 source = "registry+https://github.com/rust-lang/crates.io-index" 3282 3647 checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 3283 - 3284 - [[package]] 3285 - name = "syn" 3286 - version = "1.0.109" 3287 - source = "registry+https://github.com/rust-lang/crates.io-index" 3288 - checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 3289 - dependencies = [ 3290 - "proc-macro2", 3291 - "unicode-ident", 3292 - ] 3293 3648 3294 3649 [[package]] 3295 3650 name = "syn" ··· 3319 3674 dependencies = [ 3320 3675 "proc-macro2", 3321 3676 "quote", 3322 - "syn 2.0.117", 3677 + "syn", 3323 3678 ] 3324 3679 3325 3680 [[package]] ··· 3364 3719 checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" 3365 3720 3366 3721 [[package]] 3722 + name = "tendril" 3723 + version = "0.4.3" 3724 + source = "registry+https://github.com/rust-lang/crates.io-index" 3725 + checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" 3726 + dependencies = [ 3727 + "futf", 3728 + "mac", 3729 + "utf-8", 3730 + ] 3731 + 3732 + [[package]] 3367 3733 name = "thiserror" 3368 3734 version = "1.0.69" 3369 3735 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3389 3755 dependencies = [ 3390 3756 "proc-macro2", 3391 3757 "quote", 3392 - "syn 2.0.117", 3758 + "syn", 3393 3759 ] 3394 3760 3395 3761 [[package]] ··· 3400 3766 dependencies = [ 3401 3767 "proc-macro2", 3402 3768 "quote", 3403 - "syn 2.0.117", 3769 + "syn", 3404 3770 ] 3405 3771 3406 3772 [[package]] ··· 3419 3785 checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" 3420 3786 dependencies = [ 3421 3787 "deranged", 3422 - "itoa", 3423 3788 "num-conv", 3424 3789 "powerfmt", 3425 3790 "serde_core", 3426 3791 "time-core", 3427 - "time-macros", 3428 3792 ] 3429 3793 3430 3794 [[package]] ··· 3432 3796 version = "0.1.8" 3433 3797 source = "registry+https://github.com/rust-lang/crates.io-index" 3434 3798 checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" 3435 - 3436 - [[package]] 3437 - name = "time-macros" 3438 - version = "0.2.27" 3439 - source = "registry+https://github.com/rust-lang/crates.io-index" 3440 - checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" 3441 - dependencies = [ 3442 - "num-conv", 3443 - "time-core", 3444 - ] 3445 3799 3446 3800 [[package]] 3447 3801 name = "tinystr" ··· 3492 3846 dependencies = [ 3493 3847 "proc-macro2", 3494 3848 "quote", 3495 - "syn 2.0.117", 3849 + "syn", 3496 3850 ] 3497 3851 3498 3852 [[package]] ··· 3534 3888 source = "registry+https://github.com/rust-lang/crates.io-index" 3535 3889 checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" 3536 3890 dependencies = [ 3537 - "indexmap 2.14.0", 3891 + "indexmap", 3538 3892 "toml_datetime", 3539 3893 "toml_parser", 3540 3894 "winnow", ··· 3621 3975 dependencies = [ 3622 3976 "proc-macro2", 3623 3977 "quote", 3624 - "syn 2.0.117", 3978 + "syn", 3625 3979 ] 3626 3980 3627 3981 [[package]] ··· 3682 4036 dependencies = [ 3683 4037 "proc-macro2", 3684 4038 "quote", 3685 - "syn 2.0.117", 4039 + "syn", 3686 4040 ] 3687 4041 3688 4042 [[package]] 4043 + name = "triomphe" 4044 + version = "0.1.15" 4045 + source = "registry+https://github.com/rust-lang/crates.io-index" 4046 + checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" 4047 + 4048 + [[package]] 3689 4049 name = "try-lock" 3690 4050 version = "0.2.5" 3691 4051 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3743 4103 "idna", 3744 4104 "percent-encoding", 3745 4105 "serde", 3746 - "serde_derive", 3747 4106 ] 3748 4107 3749 4108 [[package]] 3750 - name = "urlencoding" 3751 - version = "2.1.3" 4109 + name = "utf-8" 4110 + version = "0.7.6" 3752 4111 source = "registry+https://github.com/rust-lang/crates.io-index" 3753 - checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" 4112 + checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 3754 4113 3755 4114 [[package]] 3756 4115 name = "utf8_iter" ··· 3862 4221 "bumpalo", 3863 4222 "proc-macro2", 3864 4223 "quote", 3865 - "syn 2.0.117", 4224 + "syn", 3866 4225 "wasm-bindgen-shared", 3867 4226 ] 3868 4227 ··· 3892 4251 checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" 3893 4252 dependencies = [ 3894 4253 "anyhow", 3895 - "indexmap 2.14.0", 4254 + "indexmap", 3896 4255 "wasm-encoder", 3897 4256 "wasmparser", 3898 4257 ] ··· 3918 4277 dependencies = [ 3919 4278 "bitflags", 3920 4279 "hashbrown 0.15.5", 3921 - "indexmap 2.14.0", 4280 + "indexmap", 3922 4281 "semver", 3923 4282 ] 3924 4283 ··· 3943 4302 ] 3944 4303 3945 4304 [[package]] 4305 + name = "webpage" 4306 + version = "2.0.1" 4307 + source = "registry+https://github.com/rust-lang/crates.io-index" 4308 + checksum = "70862efc041d46e6bbaa82bb9c34ae0596d090e86cbd14bd9e93b36ee6802eac" 4309 + dependencies = [ 4310 + "html5ever", 4311 + "markup5ever_rcdom", 4312 + "serde_json", 4313 + "url", 4314 + ] 4315 + 4316 + [[package]] 3946 4317 name = "webpki-roots" 3947 4318 version = "1.0.7" 3948 4319 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4026 4397 dependencies = [ 4027 4398 "proc-macro2", 4028 4399 "quote", 4029 - "syn 2.0.117", 4400 + "syn", 4030 4401 ] 4031 4402 4032 4403 [[package]] ··· 4037 4408 dependencies = [ 4038 4409 "proc-macro2", 4039 4410 "quote", 4040 - "syn 2.0.117", 4411 + "syn", 4041 4412 ] 4042 4413 4043 4414 [[package]] ··· 4293 4664 dependencies = [ 4294 4665 "anyhow", 4295 4666 "heck 0.5.0", 4296 - "indexmap 2.14.0", 4667 + "indexmap", 4297 4668 "prettyplease", 4298 - "syn 2.0.117", 4669 + "syn", 4299 4670 "wasm-metadata", 4300 4671 "wit-bindgen-core", 4301 4672 "wit-component", ··· 4311 4682 "prettyplease", 4312 4683 "proc-macro2", 4313 4684 "quote", 4314 - "syn 2.0.117", 4685 + "syn", 4315 4686 "wit-bindgen-core", 4316 4687 "wit-bindgen-rust", 4317 4688 ] ··· 4324 4695 dependencies = [ 4325 4696 "anyhow", 4326 4697 "bitflags", 4327 - "indexmap 2.14.0", 4698 + "indexmap", 4328 4699 "log", 4329 4700 "serde", 4330 4701 "serde_derive", ··· 4343 4714 dependencies = [ 4344 4715 "anyhow", 4345 4716 "id-arena", 4346 - "indexmap 2.14.0", 4717 + "indexmap", 4347 4718 "log", 4348 4719 "semver", 4349 4720 "serde", ··· 4360 4731 checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" 4361 4732 4362 4733 [[package]] 4734 + name = "xml5ever" 4735 + version = "0.18.1" 4736 + source = "registry+https://github.com/rust-lang/crates.io-index" 4737 + checksum = "9bbb26405d8e919bc1547a5aa9abc95cbfa438f04844f5fdd9dc7596b748bf69" 4738 + dependencies = [ 4739 + "log", 4740 + "mac", 4741 + "markup5ever", 4742 + ] 4743 + 4744 + [[package]] 4363 4745 name = "yansi" 4364 4746 version = "1.0.1" 4365 4747 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4384 4766 dependencies = [ 4385 4767 "proc-macro2", 4386 4768 "quote", 4387 - "syn 2.0.117", 4769 + "syn", 4388 4770 "synstructure", 4389 4771 ] 4390 4772 ··· 4405 4787 dependencies = [ 4406 4788 "proc-macro2", 4407 4789 "quote", 4408 - "syn 2.0.117", 4790 + "syn", 4409 4791 ] 4410 4792 4411 4793 [[package]] ··· 4425 4807 dependencies = [ 4426 4808 "proc-macro2", 4427 4809 "quote", 4428 - "syn 2.0.117", 4810 + "syn", 4429 4811 "synstructure", 4430 4812 ] 4431 4813 ··· 4434 4816 version = "1.8.2" 4435 4817 source = "registry+https://github.com/rust-lang/crates.io-index" 4436 4818 checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" 4819 + dependencies = [ 4820 + "serde", 4821 + ] 4437 4822 4438 4823 [[package]] 4439 4824 name = "zerotrie" ··· 4465 4850 dependencies = [ 4466 4851 "proc-macro2", 4467 4852 "quote", 4468 - "syn 2.0.117", 4853 + "syn", 4469 4854 ] 4470 4855 4471 4856 [[package]]
+3 -107
Cargo.toml
··· 1 - [package] 2 - name = "porxie" 3 - description = "A correct and efficient ATProto blob proxy for secure content delivery." 4 - authors = ["Blooym"] 5 - repository = "https://codeberg.org/Blooym/porxie" 6 - homepage = "https://codeberg.org/Blooym/porxie/src/branch/main/README.md" 7 - documentation = "https://codeberg.org/Blooym/porxie/src/branch/main/README.md" 8 - license = "AGPL-3.0-or-later" 9 - version = "0.1.2" 10 - edition = "2024" 1 + [workspace] 2 + members = ["crates/porxie", "crates/lexgen"] 3 + resolver = "3" 11 4 12 5 [profile.release] 13 6 lto = true 14 7 codegen-units = 1 15 8 opt-level = 3 16 9 strip = "debuginfo" 17 - 18 - [dependencies] 19 - anyhow = { version = "1.0.102", features = ["std"], default-features = false } 20 - axum = { version = "0.8.8", features = [ 21 - "http1", 22 - "http2", 23 - "json", 24 - "matched-path", 25 - "tokio", 26 - "tower-log", 27 - "tracing", 28 - ], default-features = false } 29 - axum-extra = { version = "0.12.5", features = [ 30 - "typed-header", 31 - "tracing", 32 - ], default-features = false } 33 - bytes = { version = "1.11.1", features = ["std"], default-features = false } 34 - bytesize = { version = "2.3.1", features = ["std"], default-features = false } 35 - cid = { version = "0.11.1", features = ["std"], default-features = false } 36 - clap = { version = "4.5.60", features = [ 37 - "color", 38 - "derive", 39 - "env", 40 - "error-context", 41 - "help", 42 - "std", 43 - "suggestions", 44 - "usage", 45 - ], default-features = false } 46 - dotenvy = { version = "0.15.7", default-features = false } 47 - futures-util = { version = "0.3.32", default-features = false } 48 - humantime = { version = "2.3.0", default-features = false } 49 - infer = { version = "0.19.0", default-features = false, features = ["std"] } 50 - jacquard-common = { version = "0.9.5", default-features = false } 51 - jacquard-identity = { version = "0.9.5", features = ["tracing"] } 52 - jemallocator = "0.5.4" 53 - json-subscriber = { version = "0.2.8", default-features = false, features = [ 54 - "tracing-log", 55 - "env-filter", 56 - ] } 57 - mime = { version = "0.3.17", default-features = false } 58 - moka = { version = "0.12.14", features = [ 59 - "future", 60 - "logging", 61 - ], default-features = false } 62 - multihash-codetable = { version = "0.2.1", features = [ 63 - "sha2", 64 - # "blake3", # if it ever gets added to the spec. 65 - "std", 66 - ], default-features = false } 67 - reqwest = { version = "0.12.28", default-features = false, features = [ 68 - "http2", 69 - "system-proxy", 70 - "stream", 71 - "socks", 72 - "rustls-tls", 73 - "gzip", 74 - "brotli", 75 - "zstd", 76 - "deflate", 77 - ] } 78 - serde = { version = "1.0.228", features = [ 79 - "derive", 80 - "std", 81 - ], default-features = false } 82 - subtle = { version = "2.6", default-features = false, features = ["std"] } 83 - sysinfo = { version = "0.38.4", default-features = false, features = [ 84 - "system", 85 - ] } 86 - thiserror = { version = "2.0.18", default-features = false, features = ["std"] } 87 - tokio = { version = "1.50.0", default-features = false, features = [ 88 - "macros", 89 - "rt-multi-thread", 90 - "signal", 91 - "net", 92 - ] } 93 - tower-http = { version = "0.6.8", features = [ 94 - "catch-panic", 95 - "normalize-path", 96 - "trace", 97 - "timeout", 98 - "tracing", 99 - ], default-features = false } 100 - tracing = { version = "0.1.44", features = [ 101 - "attributes", 102 - "std", 103 - ], default-features = false } 104 - tracing-subscriber = { version = "0.3.22", features = [ 105 - "ansi", 106 - "env-filter", 107 - "fmt", 108 - "parking_lot", 109 - "smallvec", 110 - "std", 111 - "tracing", 112 - "tracing-log", 113 - ], default-features = false }
+34 -38
README.md
··· 10 10 11 11 ## Features 12 12 13 - - Blob validation - verifies blob content matches its CID and rejects invalid/tampered content. 14 - - Secure serving - blobs are always served with secure headers to help improve end-user security. 15 - - MIME filtering - detects blob content MIME-types and enforces an optional allowlist of permitted types. 16 - - Policy enforcement - optionally integrate with an external policy service (like an AppView) to control which blobs can be served. 17 - - In-memory cache - configurable in-memory caching for fast repeat access with support for manual cache purging via authenticated HTTP DELETE. 18 - 19 - ## Routes 20 - 21 - - [GET] `/{did}/{cid}`: Resolve and fetch a blob from its origin. 22 - - [DELETE] `/cache/{cid or did}`: Invalidate all valid cache items for a specific blob CID or for an entire user DID. Requires configured bearer auth token. 13 + - Blob validation: verifies blob content matches its CID and rejects invalid/tampered content. 14 + - Secure serving: blobs are always served with secure headers to help improve end-user security. 15 + - MIME filtering: detects blob content MIME-types and enforces an optional allowlist of permitted types. 16 + - Policy enforcement: optionally integrate with an external policy service (like an AppView) to control which blobs can be served. 17 + - In-memory cache: configurable in-memory caching for fast repeat access with support for manual cache purging via authenticated HTTP DELETE. 23 18 24 19 ## Usage 25 20 26 21 > [!NOTE] 27 - > Porxie does not handle TLS, so it should be placed behind a reverse proxy like [Caddy](https://caddyserver.com), [Traefik](https://traefik.io/traefik), or [NGINX](https://nginx.org). Ensure that any intermediaries between Porxie and the client pass through the `Cache-Control`, `Content-Security-Policy` and `Content-Disposition` headers, or otherwise set them securely. 28 - > 29 - > Putting a CDN in front of Porxie is also recommended for better long-term caching and worldwide latency. 22 + > Porxie does not handle TLS, so it should be placed behind a reverse proxy like [Caddy](https://caddyserver.com), [Traefik](https://traefik.io/traefik), or [NGINX](https://nginx.org). It is also recommended to use a dedicated caching layer in-between Porxie and your clients such as Varnish, Cloudflare, or similar. 23 + > 24 + > Please ensure that any intermediary services between Porxie and the client pass through the following headers or set them the same as Porxie does: 25 + > - `Content-Type` (if unmodified by the service) 26 + > - `Cache-Control` 27 + > - `Content-Security-Policy` 28 + > - `Content-Disposition` 29 + > - `X-Content-Type-Options` 30 30 31 31 ### Run: Binary 32 32 ··· 44 44 porxie 45 45 ``` 46 46 47 - ### Run: Docker 48 - 49 - To run Porxie with the Docker CLI and default settings, use the following command: 50 - 51 - ```sh 52 - docker run -d \ 53 - --name porxie \ 54 - --restart unless-stopped \ 55 - -p 6314:6314 \ 56 - blooym/porxie:latest 57 - ``` 58 - 59 47 ### Run: Docker Compose 60 48 61 49 To run Porxie with Docker Compose, you can start with the following `compose.yml` template: ··· 78 66 79 67 To run Porxie with Nix, you can use the [package](https://search.nixos.org/packages?channel=unstable&query=porxie) or [NixOS module](https://search.nixos.org/options?channel=unstable&query=porxie) provided directly in nixpkgs. 80 68 69 + ## Routes 70 + 71 + - [GET] `/{did}/{cid}`: Fetch a blob either from cache or origin. 72 + - [GET] `/xrpc/net.dollware.porxie.getBlob?did=<did>&cid=<cid>`: Compatibility alias of the fetch blob endpoint. 73 + - [POST] `/xrpc/net.dollware.porxie.clearActorCache?did=<did>`: Clear all cached items relating to an actor DID. 74 + - [POST] `/xrpc/net.dollware.porxie.clearBlobCache?cid=<cid>`: Clear all cache items relating to a blob CID. 75 + 76 + 81 77 ## Policy Service 82 78 83 79 Porxie can optionally check with an external HTTP service before serving any blob. You build and run this service yourself - Porxie just calls it and acts on the response. This is useful for things like content takedowns or blob allow lists. ··· 111 107 [env: PORXIE_SERVER_ADDRESS=] 112 108 [default: ip:127.0.0.1:6314] 113 109 114 - --server-auth-token <SA_SERVER_AUTH_TOKEN> 115 - Bearer token for authenticating admin requests. 110 + --server-admin-password <SA_SERVER_ADMIN_PASSWORD> 111 + Admin password for authenticating privileged requests. 116 112 117 113 When unset, all authenticated endpoints will reject requests with HTTP 401. 118 114 119 - [env: PORXIE_SERVER_AUTH_TOKEN=] 115 + Authenticated requests always expect the username `admin` as per specification. 116 + 117 + [env: PORXIE_SERVER_ADMIN_TOKEN=] 120 118 ``` 121 119 122 120 ### Blob ··· 139 137 140 138 --blob-max-size <BA_BLOB_MAX_SIZE> 141 139 Maximum blob size that can be fetched and served. 142 - 140 + 143 141 Blobs that exceed this limit will return HTTP 413. 144 - 142 + 145 143 The minimum value is 512kb and the maximum is the system's total memory. 146 144 147 145 [env: PORXIE_BLOB_MAX_SIZE=] ··· 155 153 cleared manually for changes to take effect quickly. 156 154 157 155 [env: PORXIE_BLOB_CACHE_HEADER=] 158 - [default: "public, max-age=604800, must-revalidate, immutable"] 156 + [default: "public, max-age=604800, immutable"] 159 157 160 158 --blob-processing-timeout <BA_BLOB_PROCESSING_TIMEOUT> 161 159 Maximum duration a blob can be processed by this server before aborting ··· 209 207 ``` 210 208 --cache-allocation <CA_CACHE_ALLOCATION> 211 209 Total memory allocation for the internal cache. 212 - 210 + 213 211 Blobs are cached using an LFU policy. The most frequently requested blobs are kept longest when the cache approaches its limit. 214 - 212 + 215 213 For production deployments, a CDN or caching layer in front of this server is recommended for lower latency and better global availability. 216 - 214 + 217 215 The minimum value is 8mb and the maximum is the system's total memory. 218 216 219 217 [env: PORXIE_CACHE_ALLOCATION=] ··· 266 264 267 265 As pipes are used as a delimiter, they cannot be contained in headers. 268 266 269 - Example (cli): '--policy-request-headers "Authorization: Bearer token" 270 - --policy-request-headers "X-Api-Key: your-key"' 267 + Example (cli): '--policy-request-headers "X-Hello: world" --policy-request-headers "X-Foo: bar"' 271 268 272 - Example (env): 'PORXIE_POLICY_REQUEST_HEADERS="Authorization: Bearer 273 - token|X-Api-Key: your-key"' 269 + Example (env): 'PORXIE_POLICY_REQUEST_HEADERS="X-Hello: world|X-Foo: bar"' 274 270 275 271 [env: PORXIE_POLICY_REQUEST_HEADERS=] 276 272
+16
crates/lexgen/Cargo.toml
··· 1 + [package] 2 + name = "lexgen" 3 + version = "0.0.0" 4 + edition = "2024" 5 + 6 + [features] 7 + default = ["net_dollware"] 8 + net_dollware = [] 9 + 10 + [dependencies] 11 + jacquard-common = { version = "0.11.0", default-features = false } 12 + jacquard-derive = { version = "0.11.0", default-features = false } 13 + jacquard-lexicon = { version = "0.11.1", default-features = false } 14 + miette = { version = "7.6.0", default-features = false } 15 + serde = { version = "1.0.228", default-features = false } 16 + thiserror = { version = "2.0.18", default-features = false }
+32
crates/lexgen/lexicons/net/dollware/porxie/clearActorCache.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "net.dollware.porxie.clearActorCache", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "input": { 8 + "encoding": "application/json", 9 + "schema": { 10 + "type": "object", 11 + "required": [ 12 + "did" 13 + ], 14 + "properties": { 15 + "did": { 16 + "type": "string", 17 + "format": "did", 18 + "description": "The DID of the account." 19 + } 20 + } 21 + } 22 + }, 23 + "output": { 24 + "encoding": "application/json", 25 + "schema": { 26 + "type": "object", 27 + "properties": {} 28 + } 29 + } 30 + } 31 + } 32 + }
+32
crates/lexgen/lexicons/net/dollware/porxie/clearBlobCache.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "net.dollware.porxie.clearBlobCache", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "input": { 8 + "encoding": "application/json", 9 + "schema": { 10 + "type": "object", 11 + "required": [ 12 + "cid" 13 + ], 14 + "properties": { 15 + "cid": { 16 + "type": "string", 17 + "format": "cid", 18 + "description": "The CID of the blob." 19 + } 20 + } 21 + } 22 + }, 23 + "output": { 24 + "encoding": "application/json", 25 + "schema": { 26 + "type": "object", 27 + "properties": {} 28 + } 29 + } 30 + } 31 + } 32 + }
+31
crates/lexgen/lexicons/net/dollware/porxie/getBlob.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "net.dollware.porxie.getBlob", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "parameters": { 8 + "type": "params", 9 + "required": [ 10 + "did", 11 + "cid" 12 + ], 13 + "properties": { 14 + "did": { 15 + "type": "string", 16 + "format": "did", 17 + "description": "The DID of the account." 18 + }, 19 + "cid": { 20 + "type": "string", 21 + "format": "cid", 22 + "description": "The CID of the blob." 23 + } 24 + } 25 + }, 26 + "output": { 27 + "encoding": "*/*" 28 + } 29 + } 30 + } 31 + }
+72
crates/lexgen/lexicons/net/dollware/porxie/getBlobPolicy.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "net.dollware.porxie.getBlobPolicy", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "parameters": { 8 + "type": "params", 9 + "required": [ 10 + "did", 11 + "cid" 12 + ], 13 + "properties": { 14 + "did": { 15 + "type": "string", 16 + "format": "did", 17 + "description": "The DID of the account." 18 + }, 19 + "cid": { 20 + "type": "string", 21 + "format": "cid", 22 + "description": "The CID of the blob." 23 + } 24 + } 25 + }, 26 + "output": { 27 + "encoding": "application/json", 28 + "schema": { 29 + "type": "object", 30 + "required": [ 31 + "policy" 32 + ], 33 + "properties": { 34 + "policy": { 35 + "type": "union", 36 + "refs": [ 37 + "#allowed", 38 + "#restricted", 39 + "#unlisted" 40 + ] 41 + } 42 + } 43 + } 44 + } 45 + }, 46 + "allowed": { 47 + "type": "object", 48 + "description": "Blob is allowed to be served.", 49 + "properties": {} 50 + }, 51 + "restricted": { 52 + "type": "object", 53 + "description": "Blob is explicitly restricted. It may have been removed due to moderation reasons.", 54 + "properties": { 55 + "reason": { 56 + "type": "string", 57 + "description": "An optional reason provided for this policy being applied to provide context to the requesting service." 58 + } 59 + } 60 + }, 61 + "unlisted": { 62 + "type": "object", 63 + "description": "Blob is not being served at operator discretion. It may not meet the requirements for the service.", 64 + "properties": { 65 + "reason": { 66 + "type": "string", 67 + "description": "An optional reason provided for this policy being applied to provide context to the requesting service." 68 + } 69 + } 70 + } 71 + } 72 + }
+45
crates/lexgen/src/builder_types.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + /// Marker type indicating a builder field has been set 7 + pub struct Set<T>(pub T); 8 + impl<T> Set<T> { 9 + /// Extract the inner value 10 + #[inline] 11 + pub fn into_inner(self) -> T { 12 + self.0 13 + } 14 + } 15 + 16 + /// Marker type indicating a builder field has not been set 17 + pub struct Unset; 18 + /// Trait indicating a builder field is set (has a value) 19 + 20 + #[jacquard_common::deps::codegen::rustversion::attr( 21 + since(1.78.0), 22 + diagnostic::on_unimplemented( 23 + message = "the field `{Self}` was not set, but this method requires it to be set", 24 + label = "the field `{Self}` was not set" 25 + ) 26 + )] 27 + pub trait IsSet: private::Sealed {} 28 + /// Trait indicating a builder field is unset (no value yet) 29 + 30 + #[jacquard_common::deps::codegen::rustversion::attr( 31 + since(1.78.0), 32 + diagnostic::on_unimplemented( 33 + message = "the field `{Self}` was already set, but this method requires it to be unset", 34 + label = "the field `{Self}` was already set" 35 + ) 36 + )] 37 + pub trait IsUnset: private::Sealed {} 38 + impl<T> IsSet for Set<T> {} 39 + impl IsUnset for Unset {} 40 + mod private { 41 + /// Sealed trait to prevent external implementations 42 + pub trait Sealed {} 43 + impl<T> Sealed for super::Set<T> {} 44 + impl Sealed for super::Unset {} 45 + }
+11
crates/lexgen/src/lib.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + extern crate alloc; 7 + pub mod builder_types; 8 + 9 + 10 + #[cfg(feature = "net_dollware")] 11 + pub mod net_dollware;
+6
crates/lexgen/src/net_dollware.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + pub mod porxie;
+9
crates/lexgen/src/net_dollware/porxie.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + pub mod clear_actor_cache; 7 + pub mod clear_blob_cache; 8 + pub mod get_blob; 9 + pub mod get_blob_policy;
+160
crates/lexgen/src/net_dollware/porxie/clear_actor_cache.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: net.dollware.porxie.clearActorCache 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[allow(unused_imports)] 9 + use alloc::collections::BTreeMap; 10 + 11 + #[allow(unused_imports)] 12 + use core::marker::PhantomData; 13 + use jacquard_common::types::string::Did; 14 + use jacquard_derive::{IntoStatic, lexicon}; 15 + use serde::{Serialize, Deserialize}; 16 + 17 + #[lexicon] 18 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)] 19 + #[serde(rename_all = "camelCase")] 20 + pub struct ClearActorCache<'a> { 21 + ///The DID of the account. 22 + #[serde(borrow)] 23 + pub did: Did<'a>, 24 + } 25 + 26 + 27 + #[lexicon] 28 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic, Default)] 29 + #[serde(rename_all = "camelCase")] 30 + pub struct ClearActorCacheOutput<'a> {} 31 + /// Response type for net.dollware.porxie.clearActorCache 32 + pub struct ClearActorCacheResponse; 33 + impl jacquard_common::xrpc::XrpcResp for ClearActorCacheResponse { 34 + const NSID: &'static str = "net.dollware.porxie.clearActorCache"; 35 + const ENCODING: &'static str = "application/json"; 36 + type Output<'de> = ClearActorCacheOutput<'de>; 37 + type Err<'de> = jacquard_common::xrpc::GenericError<'de>; 38 + } 39 + 40 + impl<'a> jacquard_common::xrpc::XrpcRequest for ClearActorCache<'a> { 41 + const NSID: &'static str = "net.dollware.porxie.clearActorCache"; 42 + const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure( 43 + "application/json", 44 + ); 45 + type Response = ClearActorCacheResponse; 46 + } 47 + 48 + /// Endpoint type for net.dollware.porxie.clearActorCache 49 + pub struct ClearActorCacheRequest; 50 + impl jacquard_common::xrpc::XrpcEndpoint for ClearActorCacheRequest { 51 + const PATH: &'static str = "/xrpc/net.dollware.porxie.clearActorCache"; 52 + const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure( 53 + "application/json", 54 + ); 55 + type Request<'de> = ClearActorCache<'de>; 56 + type Response = ClearActorCacheResponse; 57 + } 58 + 59 + pub mod clear_actor_cache_state { 60 + 61 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 62 + #[allow(unused)] 63 + use ::core::marker::PhantomData; 64 + mod sealed { 65 + pub trait Sealed {} 66 + } 67 + /// State trait tracking which required fields have been set 68 + pub trait State: sealed::Sealed { 69 + type Did; 70 + } 71 + /// Empty state - all required fields are unset 72 + pub struct Empty(()); 73 + impl sealed::Sealed for Empty {} 74 + impl State for Empty { 75 + type Did = Unset; 76 + } 77 + ///State transition - sets the `did` field to Set 78 + pub struct SetDid<S: State = Empty>(PhantomData<fn() -> S>); 79 + impl<S: State> sealed::Sealed for SetDid<S> {} 80 + impl<S: State> State for SetDid<S> { 81 + type Did = Set<members::did>; 82 + } 83 + /// Marker types for field names 84 + #[allow(non_camel_case_types)] 85 + pub mod members { 86 + ///Marker type for the `did` field 87 + pub struct did(()); 88 + } 89 + } 90 + 91 + /// Builder for constructing an instance of this type 92 + pub struct ClearActorCacheBuilder<'a, S: clear_actor_cache_state::State> { 93 + _state: PhantomData<fn() -> S>, 94 + _fields: (Option<Did<'a>>,), 95 + _lifetime: PhantomData<&'a ()>, 96 + } 97 + 98 + impl<'a> ClearActorCache<'a> { 99 + /// Create a new builder for this type 100 + pub fn new() -> ClearActorCacheBuilder<'a, clear_actor_cache_state::Empty> { 101 + ClearActorCacheBuilder::new() 102 + } 103 + } 104 + 105 + impl<'a> ClearActorCacheBuilder<'a, clear_actor_cache_state::Empty> { 106 + /// Create a new builder with all fields unset 107 + pub fn new() -> Self { 108 + ClearActorCacheBuilder { 109 + _state: PhantomData, 110 + _fields: (None,), 111 + _lifetime: PhantomData, 112 + } 113 + } 114 + } 115 + 116 + impl<'a, S> ClearActorCacheBuilder<'a, S> 117 + where 118 + S: clear_actor_cache_state::State, 119 + S::Did: clear_actor_cache_state::IsUnset, 120 + { 121 + /// Set the `did` field (required) 122 + pub fn did( 123 + mut self, 124 + value: impl Into<Did<'a>>, 125 + ) -> ClearActorCacheBuilder<'a, clear_actor_cache_state::SetDid<S>> { 126 + self._fields.0 = Option::Some(value.into()); 127 + ClearActorCacheBuilder { 128 + _state: PhantomData, 129 + _fields: self._fields, 130 + _lifetime: PhantomData, 131 + } 132 + } 133 + } 134 + 135 + impl<'a, S> ClearActorCacheBuilder<'a, S> 136 + where 137 + S: clear_actor_cache_state::State, 138 + S::Did: clear_actor_cache_state::IsSet, 139 + { 140 + /// Build the final struct 141 + pub fn build(self) -> ClearActorCache<'a> { 142 + ClearActorCache { 143 + did: self._fields.0.unwrap(), 144 + extra_data: Default::default(), 145 + } 146 + } 147 + /// Build the final struct with custom extra_data 148 + pub fn build_with_data( 149 + self, 150 + extra_data: BTreeMap< 151 + jacquard_common::deps::smol_str::SmolStr, 152 + jacquard_common::types::value::Data<'a>, 153 + >, 154 + ) -> ClearActorCache<'a> { 155 + ClearActorCache { 156 + did: self._fields.0.unwrap(), 157 + extra_data: Some(extra_data), 158 + } 159 + } 160 + }
+160
crates/lexgen/src/net_dollware/porxie/clear_blob_cache.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: net.dollware.porxie.clearBlobCache 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[allow(unused_imports)] 9 + use alloc::collections::BTreeMap; 10 + 11 + #[allow(unused_imports)] 12 + use core::marker::PhantomData; 13 + use jacquard_common::types::string::Cid; 14 + use jacquard_derive::{IntoStatic, lexicon}; 15 + use serde::{Serialize, Deserialize}; 16 + 17 + #[lexicon] 18 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)] 19 + #[serde(rename_all = "camelCase")] 20 + pub struct ClearBlobCache<'a> { 21 + ///The CID of the blob. 22 + #[serde(borrow)] 23 + pub cid: Cid<'a>, 24 + } 25 + 26 + 27 + #[lexicon] 28 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic, Default)] 29 + #[serde(rename_all = "camelCase")] 30 + pub struct ClearBlobCacheOutput<'a> {} 31 + /// Response type for net.dollware.porxie.clearBlobCache 32 + pub struct ClearBlobCacheResponse; 33 + impl jacquard_common::xrpc::XrpcResp for ClearBlobCacheResponse { 34 + const NSID: &'static str = "net.dollware.porxie.clearBlobCache"; 35 + const ENCODING: &'static str = "application/json"; 36 + type Output<'de> = ClearBlobCacheOutput<'de>; 37 + type Err<'de> = jacquard_common::xrpc::GenericError<'de>; 38 + } 39 + 40 + impl<'a> jacquard_common::xrpc::XrpcRequest for ClearBlobCache<'a> { 41 + const NSID: &'static str = "net.dollware.porxie.clearBlobCache"; 42 + const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure( 43 + "application/json", 44 + ); 45 + type Response = ClearBlobCacheResponse; 46 + } 47 + 48 + /// Endpoint type for net.dollware.porxie.clearBlobCache 49 + pub struct ClearBlobCacheRequest; 50 + impl jacquard_common::xrpc::XrpcEndpoint for ClearBlobCacheRequest { 51 + const PATH: &'static str = "/xrpc/net.dollware.porxie.clearBlobCache"; 52 + const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure( 53 + "application/json", 54 + ); 55 + type Request<'de> = ClearBlobCache<'de>; 56 + type Response = ClearBlobCacheResponse; 57 + } 58 + 59 + pub mod clear_blob_cache_state { 60 + 61 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 62 + #[allow(unused)] 63 + use ::core::marker::PhantomData; 64 + mod sealed { 65 + pub trait Sealed {} 66 + } 67 + /// State trait tracking which required fields have been set 68 + pub trait State: sealed::Sealed { 69 + type Cid; 70 + } 71 + /// Empty state - all required fields are unset 72 + pub struct Empty(()); 73 + impl sealed::Sealed for Empty {} 74 + impl State for Empty { 75 + type Cid = Unset; 76 + } 77 + ///State transition - sets the `cid` field to Set 78 + pub struct SetCid<S: State = Empty>(PhantomData<fn() -> S>); 79 + impl<S: State> sealed::Sealed for SetCid<S> {} 80 + impl<S: State> State for SetCid<S> { 81 + type Cid = Set<members::cid>; 82 + } 83 + /// Marker types for field names 84 + #[allow(non_camel_case_types)] 85 + pub mod members { 86 + ///Marker type for the `cid` field 87 + pub struct cid(()); 88 + } 89 + } 90 + 91 + /// Builder for constructing an instance of this type 92 + pub struct ClearBlobCacheBuilder<'a, S: clear_blob_cache_state::State> { 93 + _state: PhantomData<fn() -> S>, 94 + _fields: (Option<Cid<'a>>,), 95 + _lifetime: PhantomData<&'a ()>, 96 + } 97 + 98 + impl<'a> ClearBlobCache<'a> { 99 + /// Create a new builder for this type 100 + pub fn new() -> ClearBlobCacheBuilder<'a, clear_blob_cache_state::Empty> { 101 + ClearBlobCacheBuilder::new() 102 + } 103 + } 104 + 105 + impl<'a> ClearBlobCacheBuilder<'a, clear_blob_cache_state::Empty> { 106 + /// Create a new builder with all fields unset 107 + pub fn new() -> Self { 108 + ClearBlobCacheBuilder { 109 + _state: PhantomData, 110 + _fields: (None,), 111 + _lifetime: PhantomData, 112 + } 113 + } 114 + } 115 + 116 + impl<'a, S> ClearBlobCacheBuilder<'a, S> 117 + where 118 + S: clear_blob_cache_state::State, 119 + S::Cid: clear_blob_cache_state::IsUnset, 120 + { 121 + /// Set the `cid` field (required) 122 + pub fn cid( 123 + mut self, 124 + value: impl Into<Cid<'a>>, 125 + ) -> ClearBlobCacheBuilder<'a, clear_blob_cache_state::SetCid<S>> { 126 + self._fields.0 = Option::Some(value.into()); 127 + ClearBlobCacheBuilder { 128 + _state: PhantomData, 129 + _fields: self._fields, 130 + _lifetime: PhantomData, 131 + } 132 + } 133 + } 134 + 135 + impl<'a, S> ClearBlobCacheBuilder<'a, S> 136 + where 137 + S: clear_blob_cache_state::State, 138 + S::Cid: clear_blob_cache_state::IsSet, 139 + { 140 + /// Build the final struct 141 + pub fn build(self) -> ClearBlobCache<'a> { 142 + ClearBlobCache { 143 + cid: self._fields.0.unwrap(), 144 + extra_data: Default::default(), 145 + } 146 + } 147 + /// Build the final struct with custom extra_data 148 + pub fn build_with_data( 149 + self, 150 + extra_data: BTreeMap< 151 + jacquard_common::deps::smol_str::SmolStr, 152 + jacquard_common::types::value::Data<'a>, 153 + >, 154 + ) -> ClearBlobCache<'a> { 155 + ClearBlobCache { 156 + cid: self._fields.0.unwrap(), 157 + extra_data: Some(extra_data), 158 + } 159 + } 160 + }
+190
crates/lexgen/src/net_dollware/porxie/get_blob.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: net.dollware.porxie.getBlob 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[allow(unused_imports)] 9 + use core::marker::PhantomData; 10 + use jacquard_common::deps::bytes::Bytes; 11 + use jacquard_common::types::string::{Did, Cid}; 12 + use jacquard_derive::IntoStatic; 13 + use serde::{Serialize, Deserialize}; 14 + 15 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)] 16 + #[serde(rename_all = "camelCase")] 17 + pub struct GetBlob<'a> { 18 + #[serde(borrow)] 19 + pub cid: Cid<'a>, 20 + #[serde(borrow)] 21 + pub did: Did<'a>, 22 + } 23 + 24 + 25 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)] 26 + #[serde(rename_all = "camelCase")] 27 + pub struct GetBlobOutput { 28 + pub body: Bytes, 29 + } 30 + 31 + /// Response type for net.dollware.porxie.getBlob 32 + pub struct GetBlobResponse; 33 + impl jacquard_common::xrpc::XrpcResp for GetBlobResponse { 34 + const NSID: &'static str = "net.dollware.porxie.getBlob"; 35 + const ENCODING: &'static str = "*/*"; 36 + type Output<'de> = GetBlobOutput; 37 + type Err<'de> = jacquard_common::xrpc::GenericError<'de>; 38 + fn encode_output( 39 + output: &Self::Output<'_>, 40 + ) -> Result<Vec<u8>, jacquard_common::xrpc::EncodeError> { 41 + Ok(output.body.to_vec()) 42 + } 43 + fn decode_output<'de>( 44 + body: &'de [u8], 45 + ) -> Result<Self::Output<'de>, jacquard_common::error::DecodeError> 46 + where 47 + Self::Output<'de>: serde::Deserialize<'de>, 48 + { 49 + Ok(GetBlobOutput { 50 + body: jacquard_common::deps::bytes::Bytes::copy_from_slice(body), 51 + }) 52 + } 53 + } 54 + 55 + impl<'a> jacquard_common::xrpc::XrpcRequest for GetBlob<'a> { 56 + const NSID: &'static str = "net.dollware.porxie.getBlob"; 57 + const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Query; 58 + type Response = GetBlobResponse; 59 + } 60 + 61 + /// Endpoint type for net.dollware.porxie.getBlob 62 + pub struct GetBlobRequest; 63 + impl jacquard_common::xrpc::XrpcEndpoint for GetBlobRequest { 64 + const PATH: &'static str = "/xrpc/net.dollware.porxie.getBlob"; 65 + const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Query; 66 + type Request<'de> = GetBlob<'de>; 67 + type Response = GetBlobResponse; 68 + } 69 + 70 + pub mod get_blob_state { 71 + 72 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 73 + #[allow(unused)] 74 + use ::core::marker::PhantomData; 75 + mod sealed { 76 + pub trait Sealed {} 77 + } 78 + /// State trait tracking which required fields have been set 79 + pub trait State: sealed::Sealed { 80 + type Cid; 81 + type Did; 82 + } 83 + /// Empty state - all required fields are unset 84 + pub struct Empty(()); 85 + impl sealed::Sealed for Empty {} 86 + impl State for Empty { 87 + type Cid = Unset; 88 + type Did = Unset; 89 + } 90 + ///State transition - sets the `cid` field to Set 91 + pub struct SetCid<S: State = Empty>(PhantomData<fn() -> S>); 92 + impl<S: State> sealed::Sealed for SetCid<S> {} 93 + impl<S: State> State for SetCid<S> { 94 + type Cid = Set<members::cid>; 95 + type Did = S::Did; 96 + } 97 + ///State transition - sets the `did` field to Set 98 + pub struct SetDid<S: State = Empty>(PhantomData<fn() -> S>); 99 + impl<S: State> sealed::Sealed for SetDid<S> {} 100 + impl<S: State> State for SetDid<S> { 101 + type Cid = S::Cid; 102 + type Did = Set<members::did>; 103 + } 104 + /// Marker types for field names 105 + #[allow(non_camel_case_types)] 106 + pub mod members { 107 + ///Marker type for the `cid` field 108 + pub struct cid(()); 109 + ///Marker type for the `did` field 110 + pub struct did(()); 111 + } 112 + } 113 + 114 + /// Builder for constructing an instance of this type 115 + pub struct GetBlobBuilder<'a, S: get_blob_state::State> { 116 + _state: PhantomData<fn() -> S>, 117 + _fields: (Option<Cid<'a>>, Option<Did<'a>>), 118 + _lifetime: PhantomData<&'a ()>, 119 + } 120 + 121 + impl<'a> GetBlob<'a> { 122 + /// Create a new builder for this type 123 + pub fn new() -> GetBlobBuilder<'a, get_blob_state::Empty> { 124 + GetBlobBuilder::new() 125 + } 126 + } 127 + 128 + impl<'a> GetBlobBuilder<'a, get_blob_state::Empty> { 129 + /// Create a new builder with all fields unset 130 + pub fn new() -> Self { 131 + GetBlobBuilder { 132 + _state: PhantomData, 133 + _fields: (None, None), 134 + _lifetime: PhantomData, 135 + } 136 + } 137 + } 138 + 139 + impl<'a, S> GetBlobBuilder<'a, S> 140 + where 141 + S: get_blob_state::State, 142 + S::Cid: get_blob_state::IsUnset, 143 + { 144 + /// Set the `cid` field (required) 145 + pub fn cid( 146 + mut self, 147 + value: impl Into<Cid<'a>>, 148 + ) -> GetBlobBuilder<'a, get_blob_state::SetCid<S>> { 149 + self._fields.0 = Option::Some(value.into()); 150 + GetBlobBuilder { 151 + _state: PhantomData, 152 + _fields: self._fields, 153 + _lifetime: PhantomData, 154 + } 155 + } 156 + } 157 + 158 + impl<'a, S> GetBlobBuilder<'a, S> 159 + where 160 + S: get_blob_state::State, 161 + S::Did: get_blob_state::IsUnset, 162 + { 163 + /// Set the `did` field (required) 164 + pub fn did( 165 + mut self, 166 + value: impl Into<Did<'a>>, 167 + ) -> GetBlobBuilder<'a, get_blob_state::SetDid<S>> { 168 + self._fields.1 = Option::Some(value.into()); 169 + GetBlobBuilder { 170 + _state: PhantomData, 171 + _fields: self._fields, 172 + _lifetime: PhantomData, 173 + } 174 + } 175 + } 176 + 177 + impl<'a, S> GetBlobBuilder<'a, S> 178 + where 179 + S: get_blob_state::State, 180 + S::Cid: get_blob_state::IsSet, 181 + S::Did: get_blob_state::IsSet, 182 + { 183 + /// Build the final struct 184 + pub fn build(self) -> GetBlob<'a> { 185 + GetBlob { 186 + cid: self._fields.0.unwrap(), 187 + did: self._fields.1.unwrap(), 188 + } 189 + } 190 + }
+400
crates/lexgen/src/net_dollware/porxie/get_blob_policy.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: net.dollware.porxie.getBlobPolicy 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[allow(unused_imports)] 9 + use alloc::collections::BTreeMap; 10 + 11 + #[allow(unused_imports)] 12 + use core::marker::PhantomData; 13 + use jacquard_common::CowStr; 14 + 15 + #[allow(unused_imports)] 16 + use jacquard_common::deps::codegen::unicode_segmentation::UnicodeSegmentation; 17 + use jacquard_common::types::string::{Did, Cid}; 18 + use jacquard_derive::{IntoStatic, lexicon, open_union}; 19 + use jacquard_lexicon::lexicon::LexiconDoc; 20 + use jacquard_lexicon::schema::LexiconSchema; 21 + 22 + #[allow(unused_imports)] 23 + use jacquard_lexicon::validation::{ConstraintError, ValidationPath}; 24 + use serde::{Serialize, Deserialize}; 25 + use crate::net_dollware::porxie::get_blob_policy; 26 + /// Blob is allowed to be served. 27 + 28 + #[lexicon] 29 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic, Default)] 30 + #[serde(rename_all = "camelCase")] 31 + pub struct Allowed<'a> {} 32 + 33 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)] 34 + #[serde(rename_all = "camelCase")] 35 + pub struct GetBlobPolicy<'a> { 36 + #[serde(borrow)] 37 + pub cid: Cid<'a>, 38 + #[serde(borrow)] 39 + pub did: Did<'a>, 40 + } 41 + 42 + 43 + #[lexicon] 44 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)] 45 + #[serde(rename_all = "camelCase")] 46 + pub struct GetBlobPolicyOutput<'a> { 47 + #[serde(borrow)] 48 + pub policy: GetBlobPolicyOutputPolicy<'a>, 49 + } 50 + 51 + 52 + #[open_union] 53 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)] 54 + #[serde(tag = "$type", bound(deserialize = "'de: 'a"))] 55 + pub enum GetBlobPolicyOutputPolicy<'a> { 56 + #[serde(rename = "net.dollware.porxie.getBlobPolicy#allowed")] 57 + Allowed(Box<get_blob_policy::Allowed<'a>>), 58 + #[serde(rename = "net.dollware.porxie.getBlobPolicy#restricted")] 59 + Restricted(Box<get_blob_policy::Restricted<'a>>), 60 + #[serde(rename = "net.dollware.porxie.getBlobPolicy#unlisted")] 61 + Unlisted(Box<get_blob_policy::Unlisted<'a>>), 62 + } 63 + 64 + /// Blob is explicitly restricted. It may have been removed due to moderation reasons. 65 + 66 + #[lexicon] 67 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic, Default)] 68 + #[serde(rename_all = "camelCase")] 69 + pub struct Restricted<'a> { 70 + ///An optional reason provided for this policy being applied to provide context to the requesting service. 71 + #[serde(skip_serializing_if = "Option::is_none")] 72 + #[serde(borrow)] 73 + pub reason: Option<CowStr<'a>>, 74 + } 75 + 76 + /// Blob is not being served at operator discretion. It may not meet the requirements for the service. 77 + 78 + #[lexicon] 79 + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic, Default)] 80 + #[serde(rename_all = "camelCase")] 81 + pub struct Unlisted<'a> { 82 + ///An optional reason provided for this policy being applied to provide context to the requesting service. 83 + #[serde(skip_serializing_if = "Option::is_none")] 84 + #[serde(borrow)] 85 + pub reason: Option<CowStr<'a>>, 86 + } 87 + 88 + impl<'a> LexiconSchema for Allowed<'a> { 89 + fn nsid() -> &'static str { 90 + "net.dollware.porxie.getBlobPolicy" 91 + } 92 + fn def_name() -> &'static str { 93 + "allowed" 94 + } 95 + fn lexicon_doc() -> LexiconDoc<'static> { 96 + lexicon_doc_net_dollware_porxie_get_blob_policy() 97 + } 98 + fn validate(&self) -> Result<(), ConstraintError> { 99 + Ok(()) 100 + } 101 + } 102 + 103 + /// Response type for net.dollware.porxie.getBlobPolicy 104 + pub struct GetBlobPolicyResponse; 105 + impl jacquard_common::xrpc::XrpcResp for GetBlobPolicyResponse { 106 + const NSID: &'static str = "net.dollware.porxie.getBlobPolicy"; 107 + const ENCODING: &'static str = "application/json"; 108 + type Output<'de> = GetBlobPolicyOutput<'de>; 109 + type Err<'de> = jacquard_common::xrpc::GenericError<'de>; 110 + } 111 + 112 + impl<'a> jacquard_common::xrpc::XrpcRequest for GetBlobPolicy<'a> { 113 + const NSID: &'static str = "net.dollware.porxie.getBlobPolicy"; 114 + const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Query; 115 + type Response = GetBlobPolicyResponse; 116 + } 117 + 118 + /// Endpoint type for net.dollware.porxie.getBlobPolicy 119 + pub struct GetBlobPolicyRequest; 120 + impl jacquard_common::xrpc::XrpcEndpoint for GetBlobPolicyRequest { 121 + const PATH: &'static str = "/xrpc/net.dollware.porxie.getBlobPolicy"; 122 + const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Query; 123 + type Request<'de> = GetBlobPolicy<'de>; 124 + type Response = GetBlobPolicyResponse; 125 + } 126 + 127 + impl<'a> LexiconSchema for Restricted<'a> { 128 + fn nsid() -> &'static str { 129 + "net.dollware.porxie.getBlobPolicy" 130 + } 131 + fn def_name() -> &'static str { 132 + "restricted" 133 + } 134 + fn lexicon_doc() -> LexiconDoc<'static> { 135 + lexicon_doc_net_dollware_porxie_get_blob_policy() 136 + } 137 + fn validate(&self) -> Result<(), ConstraintError> { 138 + Ok(()) 139 + } 140 + } 141 + 142 + impl<'a> LexiconSchema for Unlisted<'a> { 143 + fn nsid() -> &'static str { 144 + "net.dollware.porxie.getBlobPolicy" 145 + } 146 + fn def_name() -> &'static str { 147 + "unlisted" 148 + } 149 + fn lexicon_doc() -> LexiconDoc<'static> { 150 + lexicon_doc_net_dollware_porxie_get_blob_policy() 151 + } 152 + fn validate(&self) -> Result<(), ConstraintError> { 153 + Ok(()) 154 + } 155 + } 156 + 157 + fn lexicon_doc_net_dollware_porxie_get_blob_policy() -> LexiconDoc<'static> { 158 + #[allow(unused_imports)] 159 + use jacquard_common::{CowStr, deps::smol_str::SmolStr, types::blob::MimeType}; 160 + use jacquard_lexicon::lexicon::*; 161 + use alloc::collections::BTreeMap; 162 + LexiconDoc { 163 + lexicon: Lexicon::Lexicon1, 164 + id: CowStr::new_static("net.dollware.porxie.getBlobPolicy"), 165 + defs: { 166 + let mut map = BTreeMap::new(); 167 + map.insert( 168 + SmolStr::new_static("allowed"), 169 + LexUserType::Object(LexObject { 170 + description: Some( 171 + CowStr::new_static("Blob is allowed to be served."), 172 + ), 173 + properties: { 174 + #[allow(unused_mut)] 175 + let mut map = BTreeMap::new(); 176 + map 177 + }, 178 + ..Default::default() 179 + }), 180 + ); 181 + map.insert( 182 + SmolStr::new_static("main"), 183 + LexUserType::XrpcQuery(LexXrpcQuery { 184 + parameters: Some( 185 + LexXrpcQueryParameter::Params(LexXrpcParameters { 186 + required: Some( 187 + vec![SmolStr::new_static("did"), SmolStr::new_static("cid")], 188 + ), 189 + properties: { 190 + #[allow(unused_mut)] 191 + let mut map = BTreeMap::new(); 192 + map.insert( 193 + SmolStr::new_static("cid"), 194 + LexXrpcParametersProperty::String(LexString { 195 + description: Some( 196 + CowStr::new_static("The CID of the blob."), 197 + ), 198 + format: Some(LexStringFormat::Cid), 199 + ..Default::default() 200 + }), 201 + ); 202 + map.insert( 203 + SmolStr::new_static("did"), 204 + LexXrpcParametersProperty::String(LexString { 205 + description: Some( 206 + CowStr::new_static("The DID of the account."), 207 + ), 208 + format: Some(LexStringFormat::Did), 209 + ..Default::default() 210 + }), 211 + ); 212 + map 213 + }, 214 + ..Default::default() 215 + }), 216 + ), 217 + ..Default::default() 218 + }), 219 + ); 220 + map.insert( 221 + SmolStr::new_static("restricted"), 222 + LexUserType::Object(LexObject { 223 + description: Some( 224 + CowStr::new_static( 225 + "Blob is explicitly restricted. It may have been removed due to moderation reasons.", 226 + ), 227 + ), 228 + properties: { 229 + #[allow(unused_mut)] 230 + let mut map = BTreeMap::new(); 231 + map.insert( 232 + SmolStr::new_static("reason"), 233 + LexObjectProperty::String(LexString { 234 + description: Some( 235 + CowStr::new_static( 236 + "An optional reason provided for this policy being applied to provide context to the requesting service.", 237 + ), 238 + ), 239 + ..Default::default() 240 + }), 241 + ); 242 + map 243 + }, 244 + ..Default::default() 245 + }), 246 + ); 247 + map.insert( 248 + SmolStr::new_static("unlisted"), 249 + LexUserType::Object(LexObject { 250 + description: Some( 251 + CowStr::new_static( 252 + "Blob is not being served at operator discretion. It may not meet the requirements for the service.", 253 + ), 254 + ), 255 + properties: { 256 + #[allow(unused_mut)] 257 + let mut map = BTreeMap::new(); 258 + map.insert( 259 + SmolStr::new_static("reason"), 260 + LexObjectProperty::String(LexString { 261 + description: Some( 262 + CowStr::new_static( 263 + "An optional reason provided for this policy being applied to provide context to the requesting service.", 264 + ), 265 + ), 266 + ..Default::default() 267 + }), 268 + ); 269 + map 270 + }, 271 + ..Default::default() 272 + }), 273 + ); 274 + map 275 + }, 276 + ..Default::default() 277 + } 278 + } 279 + 280 + pub mod get_blob_policy_state { 281 + 282 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 283 + #[allow(unused)] 284 + use ::core::marker::PhantomData; 285 + mod sealed { 286 + pub trait Sealed {} 287 + } 288 + /// State trait tracking which required fields have been set 289 + pub trait State: sealed::Sealed { 290 + type Did; 291 + type Cid; 292 + } 293 + /// Empty state - all required fields are unset 294 + pub struct Empty(()); 295 + impl sealed::Sealed for Empty {} 296 + impl State for Empty { 297 + type Did = Unset; 298 + type Cid = Unset; 299 + } 300 + ///State transition - sets the `did` field to Set 301 + pub struct SetDid<S: State = Empty>(PhantomData<fn() -> S>); 302 + impl<S: State> sealed::Sealed for SetDid<S> {} 303 + impl<S: State> State for SetDid<S> { 304 + type Did = Set<members::did>; 305 + type Cid = S::Cid; 306 + } 307 + ///State transition - sets the `cid` field to Set 308 + pub struct SetCid<S: State = Empty>(PhantomData<fn() -> S>); 309 + impl<S: State> sealed::Sealed for SetCid<S> {} 310 + impl<S: State> State for SetCid<S> { 311 + type Did = S::Did; 312 + type Cid = Set<members::cid>; 313 + } 314 + /// Marker types for field names 315 + #[allow(non_camel_case_types)] 316 + pub mod members { 317 + ///Marker type for the `did` field 318 + pub struct did(()); 319 + ///Marker type for the `cid` field 320 + pub struct cid(()); 321 + } 322 + } 323 + 324 + /// Builder for constructing an instance of this type 325 + pub struct GetBlobPolicyBuilder<'a, S: get_blob_policy_state::State> { 326 + _state: PhantomData<fn() -> S>, 327 + _fields: (Option<Cid<'a>>, Option<Did<'a>>), 328 + _lifetime: PhantomData<&'a ()>, 329 + } 330 + 331 + impl<'a> GetBlobPolicy<'a> { 332 + /// Create a new builder for this type 333 + pub fn new() -> GetBlobPolicyBuilder<'a, get_blob_policy_state::Empty> { 334 + GetBlobPolicyBuilder::new() 335 + } 336 + } 337 + 338 + impl<'a> GetBlobPolicyBuilder<'a, get_blob_policy_state::Empty> { 339 + /// Create a new builder with all fields unset 340 + pub fn new() -> Self { 341 + GetBlobPolicyBuilder { 342 + _state: PhantomData, 343 + _fields: (None, None), 344 + _lifetime: PhantomData, 345 + } 346 + } 347 + } 348 + 349 + impl<'a, S> GetBlobPolicyBuilder<'a, S> 350 + where 351 + S: get_blob_policy_state::State, 352 + S::Cid: get_blob_policy_state::IsUnset, 353 + { 354 + /// Set the `cid` field (required) 355 + pub fn cid( 356 + mut self, 357 + value: impl Into<Cid<'a>>, 358 + ) -> GetBlobPolicyBuilder<'a, get_blob_policy_state::SetCid<S>> { 359 + self._fields.0 = Option::Some(value.into()); 360 + GetBlobPolicyBuilder { 361 + _state: PhantomData, 362 + _fields: self._fields, 363 + _lifetime: PhantomData, 364 + } 365 + } 366 + } 367 + 368 + impl<'a, S> GetBlobPolicyBuilder<'a, S> 369 + where 370 + S: get_blob_policy_state::State, 371 + S::Did: get_blob_policy_state::IsUnset, 372 + { 373 + /// Set the `did` field (required) 374 + pub fn did( 375 + mut self, 376 + value: impl Into<Did<'a>>, 377 + ) -> GetBlobPolicyBuilder<'a, get_blob_policy_state::SetDid<S>> { 378 + self._fields.1 = Option::Some(value.into()); 379 + GetBlobPolicyBuilder { 380 + _state: PhantomData, 381 + _fields: self._fields, 382 + _lifetime: PhantomData, 383 + } 384 + } 385 + } 386 + 387 + impl<'a, S> GetBlobPolicyBuilder<'a, S> 388 + where 389 + S: get_blob_policy_state::State, 390 + S::Did: get_blob_policy_state::IsSet, 391 + S::Cid: get_blob_policy_state::IsSet, 392 + { 393 + /// Build the final struct 394 + pub fn build(self) -> GetBlobPolicy<'a> { 395 + GetBlobPolicy { 396 + cid: self._fields.0.unwrap(), 397 + did: self._fields.1.unwrap(), 398 + } 399 + } 400 + }
+111
crates/porxie/Cargo.toml
··· 1 + [package] 2 + name = "porxie" 3 + description = "A correct and efficient ATProto blob proxy for secure content delivery." 4 + authors = ["Blooym"] 5 + repository = "https://codeberg.org/Blooym/porxie" 6 + homepage = "https://codeberg.org/Blooym/porxie/src/branch/main/README.md" 7 + documentation = "https://codeberg.org/Blooym/porxie/src/branch/main/README.md" 8 + license = "AGPL-3.0-or-later" 9 + version = "0.1.2" 10 + edition = "2024" 11 + 12 + [dependencies] 13 + anyhow = { version = "1.0.102", features = ["std"], default-features = false } 14 + axum = { version = "0.8.8", features = [ 15 + "http1", 16 + "http2", 17 + "json", 18 + "matched-path", 19 + "tokio", 20 + "tower-log", 21 + "tracing", 22 + ], default-features = false } 23 + axum-extra = { version = "0.12.5", features = [ 24 + "typed-header", 25 + "tracing", 26 + ], default-features = false } 27 + bytes = { version = "1.11.1", features = ["std"], default-features = false } 28 + bytesize = { version = "2.3.1", features = ["std"], default-features = false } 29 + cid = { version = "0.11.1", features = ["std"], default-features = false } 30 + clap = { version = "4.5.60", features = [ 31 + "color", 32 + "derive", 33 + "env", 34 + "error-context", 35 + "help", 36 + "std", 37 + "suggestions", 38 + "usage", 39 + ], default-features = false } 40 + dotenvy = { version = "0.15.7", default-features = false } 41 + futures-util = { version = "0.3.32", default-features = false } 42 + humantime = { version = "2.3.0", default-features = false } 43 + infer = { version = "0.19.0", default-features = false, features = ["std"] } 44 + jacquard-axum = { version = "0.11.0", default-features = false, features = [ 45 + "tracing", 46 + ] } 47 + jacquard-common = { version = "0.11.0", default-features = false } 48 + jacquard-identity = { version = "0.11.0", features = ["tracing"] } 49 + jemallocator = "0.5.4" 50 + json-subscriber = { version = "0.2.8", default-features = false, features = [ 51 + "tracing-log", 52 + "env-filter", 53 + ] } 54 + lexgen = { path = "../lexgen" } 55 + mime = { version = "0.3.17", default-features = false } 56 + moka = { version = "0.12.14", features = [ 57 + "future", 58 + "logging", 59 + ], default-features = false } 60 + multihash-codetable = { version = "0.2.1", features = [ 61 + "sha2", 62 + # "blake3", # if it ever gets added to the spec. 63 + "std", 64 + ], default-features = false } 65 + reqwest = { version = "0.12.28", default-features = false, features = [ 66 + "http2", 67 + "system-proxy", 68 + "stream", 69 + "socks", 70 + "rustls-tls", 71 + "gzip", 72 + "brotli", 73 + "zstd", 74 + "deflate", 75 + ] } 76 + serde = { version = "1.0.228", features = [ 77 + "derive", 78 + "std", 79 + ], default-features = false } 80 + subtle = { version = "2.6", default-features = false, features = ["std"] } 81 + sysinfo = { version = "0.38.4", default-features = false, features = [ 82 + "system", 83 + ] } 84 + thiserror = { version = "2.0.18", default-features = false, features = ["std"] } 85 + tokio = { version = "1.50.0", default-features = false, features = [ 86 + "macros", 87 + "rt-multi-thread", 88 + "signal", 89 + "net", 90 + ] } 91 + tower-http = { version = "0.6.8", features = [ 92 + "catch-panic", 93 + "normalize-path", 94 + "trace", 95 + "timeout", 96 + "tracing", 97 + ], default-features = false } 98 + tracing = { version = "0.1.44", features = [ 99 + "attributes", 100 + "std", 101 + ], default-features = false } 102 + tracing-subscriber = { version = "0.3.22", features = [ 103 + "ansi", 104 + "env-filter", 105 + "fmt", 106 + "parking_lot", 107 + "smallvec", 108 + "std", 109 + "tracing", 110 + "tracing-log", 111 + ], default-features = false }
+55
crates/porxie/src/extractors/admin_xrpc_auth.rs
··· 1 + use crate::AppState; 2 + use axum::{ 3 + extract::FromRequestParts, 4 + http::{StatusCode, request::Parts}, 5 + }; 6 + use axum_extra::{ 7 + TypedHeader, 8 + headers::{Authorization, authorization::Basic}, 9 + }; 10 + use std::sync::Arc; 11 + use subtle::ConstantTimeEq; 12 + 13 + /// Enforce a valid admin XRPC authentication and reject the request if invalid. 14 + /// 15 + /// Uses the password configured the Router's [`AppState`]. 16 + /// 17 + /// Specification: <https://atproto.com/specs/xrpc#admin-token-temporary-specification>. 18 + pub struct AdminXrpcAuth; 19 + 20 + impl FromRequestParts<Arc<AppState>> for AdminXrpcAuth { 21 + type Rejection = StatusCode; 22 + 23 + async fn from_request_parts( 24 + parts: &mut Parts, 25 + state: &Arc<AppState>, 26 + ) -> Result<Self, Self::Rejection> { 27 + let Ok(basic_auth) = 28 + TypedHeader::<Authorization<Basic>>::from_request_parts(parts, state).await 29 + else { 30 + return Err(StatusCode::UNAUTHORIZED); 31 + }; 32 + 33 + // Enforce admin as username as per specification. 34 + if basic_auth.username() != "admin" { 35 + return Err(StatusCode::UNAUTHORIZED); 36 + } 37 + 38 + // Check password with a constant time check. 39 + if !state 40 + .admin_password 41 + .as_ref() 42 + .map(|expected| { 43 + expected 44 + .as_bytes() 45 + .ct_eq(basic_auth.password().as_bytes()) 46 + .into() 47 + }) 48 + .unwrap_or(false) 49 + { 50 + return Err(StatusCode::UNAUTHORIZED); 51 + } 52 + 53 + Ok(AdminXrpcAuth) 54 + } 55 + }
+3
crates/porxie/src/extractors/mod.rs
··· 1 + mod admin_xrpc_auth; 2 + 3 + pub use admin_xrpc_auth::AdminXrpcAuth;
+19
crates/porxie/src/routes/index.rs
··· 1 + pub async fn get_index_handler() -> &'static str { 2 + r#" 3 + _____ _ 4 + | __ \ (_) 5 + | |__) |__ _ ____ ___ ___ 6 + | ___/ _ \| '__\ \/ / |/ _ \ 7 + | | | (_) | | > <| | __/ 8 + |_| \___/|_| /_/\_\_|\___| 9 + 10 + 11 + A correct and efficient ATProto blob proxy for secure content delivery. 12 + 13 + Most API routes are under /xrpc/ 14 + 15 + Links: 16 + - Repo: https://codeberg.org/Blooym/porxie 17 + - ATProto: https://atproto.com 18 + "# 19 + }
+15
crates/porxie/src/routes/mod.rs
··· 1 + mod blob; 2 + mod index; 3 + pub mod xrpc; 4 + 5 + pub use blob::get_blob_handler; 6 + pub use index::get_index_handler; 7 + 8 + /// A header value for [`header::CACHE_CONTROL`] indicating the response cannot be cached at all. 9 + const CACHE_CONTROL_NOCACHE_VALUE: &str = "must-understand, no-store"; 10 + 11 + #[derive(serde::Serialize)] 12 + pub struct ErrorResponse { 13 + error: &'static str, 14 + message: Option<&'static str>, 15 + }
+4
crates/porxie/src/routes/xrpc/mod.rs
··· 1 + mod health; 2 + pub mod net_dollware; 3 + 4 + pub use health::get_health_handler;
+1
crates/porxie/src/routes/xrpc/net_dollware/mod.rs
··· 1 + pub mod porxie;
+35
crates/porxie/src/routes/xrpc/net_dollware/porxie/clear_actor_cache.rs
··· 1 + use crate::{AppState, extractors::AdminXrpcAuth, routes::ErrorResponse}; 2 + use axum::{Json, extract::State, http::HeaderName}; 3 + use jacquard_axum::ExtractXrpc; 4 + use lexgen::net_dollware::porxie::clear_actor_cache::ClearActorCacheRequest; 5 + use reqwest::StatusCode; 6 + use std::sync::Arc; 7 + 8 + pub async fn clear_actor_cache_handler( 9 + _auth: AdminXrpcAuth, 10 + State(state): State<Arc<AppState>>, 11 + ExtractXrpc(request): ExtractXrpc<ClearActorCacheRequest>, 12 + ) -> Result< 13 + StatusCode, 14 + ( 15 + StatusCode, 16 + [(HeaderName, &'static str); 1], 17 + Json<ErrorResponse>, 18 + ), 19 + > { 20 + if let Some(ref policy_client) = state.policy_client { 21 + policy_client.invalidate_policies({ 22 + let did = request.did.clone(); 23 + move |k, _v| k.0 == did 24 + }) 25 + } 26 + state 27 + .identity_service 28 + .invalidate_did_cache(&request.did) 29 + .await; 30 + state 31 + .blob_service 32 + .invalidate_blob_ownership(move |k, _v| k.1 == request.did); 33 + 34 + Ok(StatusCode::OK) 35 + }
+49
crates/porxie/src/routes/xrpc/net_dollware/porxie/clear_blob_cache.rs
··· 1 + use crate::{ 2 + AppState, 3 + extractors::AdminXrpcAuth, 4 + routes::{CACHE_CONTROL_NOCACHE_VALUE, ErrorResponse}, 5 + types::blob_cid::BlobCid, 6 + }; 7 + use axum::{ 8 + Json, 9 + extract::State, 10 + http::{HeaderName, header}, 11 + }; 12 + use jacquard_axum::ExtractXrpc; 13 + use lexgen::net_dollware::porxie::clear_blob_cache::ClearBlobCacheRequest; 14 + use reqwest::StatusCode; 15 + use std::sync::Arc; 16 + 17 + pub async fn clear_blob_cache_handler( 18 + _auth: AdminXrpcAuth, 19 + State(state): State<Arc<AppState>>, 20 + ExtractXrpc(request): ExtractXrpc<ClearBlobCacheRequest>, 21 + ) -> Result< 22 + StatusCode, 23 + ( 24 + StatusCode, 25 + [(HeaderName, &'static str); 1], 26 + Json<ErrorResponse>, 27 + ), 28 + > { 29 + let cid = BlobCid::try_from(request.cid.as_str()).map_err(|_| { 30 + ( 31 + StatusCode::UNPROCESSABLE_ENTITY, 32 + [(header::CACHE_CONTROL, CACHE_CONTROL_NOCACHE_VALUE)], 33 + Json(ErrorResponse { 34 + error: "MalformedCid", 35 + message: Some("Invalid or unprocessable CID"), 36 + }), 37 + ) 38 + })?; 39 + 40 + if let Some(ref policy_client) = state.policy_client { 41 + policy_client.invalidate_policies(move |k, _v| k.1 == cid) 42 + } 43 + state.blob_service.invalidate_blob(&cid).await; 44 + state 45 + .blob_service 46 + .invalidate_blob_ownership(move |k, _v| k.0 == cid); 47 + 48 + Ok(StatusCode::OK) 49 + }
+22
crates/porxie/src/routes/xrpc/net_dollware/porxie/get_blob.rs
··· 1 + use crate::{AppState, routes::get_blob_handler}; 2 + use axum::{ 3 + extract::{Path, State}, 4 + response::IntoResponse, 5 + }; 6 + use jacquard_axum::ExtractXrpc; 7 + use lexgen::net_dollware::porxie::get_blob::GetBlobRequest; 8 + use std::sync::Arc; 9 + 10 + /// Compatibility layer that converts the xrpc call into a 11 + /// regular get blob request. May become the primary method 12 + /// in the future. 13 + pub async fn get_blob_handler_xrpc_compat( 14 + state: State<Arc<AppState>>, 15 + ExtractXrpc(request): ExtractXrpc<GetBlobRequest>, 16 + ) -> impl IntoResponse { 17 + get_blob_handler( 18 + Path((request.did.to_string(), request.cid.to_string())), 19 + state, 20 + ) 21 + .await 22 + }
+7
crates/porxie/src/routes/xrpc/net_dollware/porxie/mod.rs
··· 1 + mod clear_actor_cache; 2 + mod clear_blob_cache; 3 + mod get_blob; 4 + 5 + pub use clear_actor_cache::clear_actor_cache_handler; 6 + pub use clear_blob_cache::clear_blob_cache_handler; 7 + pub use get_blob::get_blob_handler_xrpc_compat;
+7 -11
src/blob_service.rs crates/porxie/src/blob_service.rs
··· 172 172 } 173 173 }; 174 174 175 - let validated_bytes = { 175 + let bytes = { 176 176 let response = self.http_client.get(blob_url).send().await.map_err(|err| { 177 177 tracing::warn!("failed to request blob from origin: {err:?}"); 178 178 BlobDownloadError::FetchFailure ··· 211 211 // 212 212 // This operation is done via spawn_blocking as creating the digest will block 213 213 // this task's executor from switching to other tasks for as long it runs. 214 + // 215 + // Passes the bytes as a return value instead of incrementing the reference count. 214 216 tokio::task::spawn_blocking({ 215 - let bytes = bytes.clone(); 216 217 let cid = *cid; 217 218 move || { 218 219 // Enabled Multihashes are set in the multihash-codetable crate features. ··· 234 235 return Err(BlobDownloadError::CidMismatch); 235 236 } 236 237 237 - Ok(()) 238 + Ok(bytes) 238 239 } 239 240 }) 240 241 .await 241 - .expect("CID computing task should not panic")?; 242 - 243 - bytes 242 + .expect("CID computing task should not panic")? 244 243 }; 245 244 246 245 // Infer MIME type from content bytes rather than headers; this is fallible ··· 248 247 // 249 248 // TODO: Merge this with the download stream process to reject bad MIMEs 250 249 // early? 251 - let mime_type = sniff_mime(&validated_bytes); 250 + let mime_type = sniff_mime(&bytes); 252 251 if !is_mime_allowed(&mime_type, allowed_mimetypes) { 253 252 tracing::debug!("blob was inferred to be a disallowed mime type: {mime_type}"); 254 253 return Err(BlobDownloadError::ForbiddenMimeType); ··· 257 256 // Mark this DID+CID pair as ownership-verified since we just fetched it from the origin. 258 257 self.ownership_cache.insert((*cid, did.clone()), ()).await; 259 258 260 - Ok(BlobData { 261 - bytes: validated_bytes, 262 - mime_type, 263 - }) 259 + Ok(BlobData { bytes, mime_type }) 264 260 }) 265 261 .await 266 262 }
src/cache.rs crates/porxie/src/cache.rs
src/http.rs crates/porxie/src/http.rs
+9 -3
src/identity_service.rs crates/porxie/src/identity_service.rs
··· 6 6 }; 7 7 use moka::{future::Cache as MokaCache, policy::EvictionPolicy}; 8 8 use reqwest::Url; 9 - use std::{sync::Arc, time::Duration}; 9 + use std::{str::FromStr, sync::Arc, time::Duration}; 10 10 use thiserror::Error; 11 11 use tracing::instrument; 12 12 ··· 57 57 .map_err(CreateIdentityServiceError::HttpClient)?, 58 58 ResolverOptions { 59 59 plc_source: PlcSource::PlcDirectory { 60 - base: options.plc_directory_url, 60 + base: jacquard_common::deps::fluent_uri::Uri::from_str( 61 + options.plc_directory_url.as_str(), 62 + ) 63 + .unwrap(), 61 64 }, 62 65 public_fallback_for_handle: true, 63 66 validate_doc_id: true, ··· 85 88 #[instrument(skip_all, fields(did = %did))] 86 89 pub async fn pds_for_did(&self, did: &Did<'static>) -> Result<Url, Arc<IdentityError>> { 87 90 self.cache 88 - .try_get_with_by_ref(did, self.resolver.pds_for_did(did)) 91 + .try_get_with_by_ref(did, async { 92 + let url = self.resolver.pds_for_did(did).await?; 93 + Ok(Url::parse(url.as_str()).unwrap()) 94 + }) 89 95 .await 90 96 } 91 97
+42 -13
src/main.rs crates/porxie/src/main.rs
··· 2 2 3 3 mod blob_service; 4 4 mod cache; 5 + mod extractors; 5 6 mod http; 6 7 mod identity_service; 7 8 mod mime; ··· 14 15 cache::compute_cache_sizes, 15 16 identity_service::{IdentityService, IdentityServiceOptions}, 16 17 policy_client::{PolicyClient, PolicyClientOptions}, 17 - routes::{delete_cache_handler, get_blob_handler, get_health_handler, get_index_handler}, 18 + routes::{ 19 + get_blob_handler, get_index_handler, 20 + xrpc::{ 21 + get_health_handler, 22 + net_dollware::porxie::{ 23 + clear_actor_cache_handler, clear_blob_cache_handler, get_blob_handler_xrpc_compat, 24 + }, 25 + }, 26 + }, 18 27 }; 19 28 use ::mime::Mime; 20 29 use anyhow::{Context, bail}; ··· 24 33 http::{HeaderName, HeaderValue, StatusCode, header}, 25 34 middleware::{self as axum_middleware, Next}, 26 35 response::Response, 27 - routing::{delete, get}, 36 + routing::{get, post}, 28 37 }; 29 38 use bytesize::ByteSize; 30 39 use clap::{Args, Parser}; ··· 116 125 )] 117 126 address: AddressType, 118 127 119 - /// Bearer token for authenticating admin requests. 128 + /// Admin password for authenticating priviledged requests. 120 129 /// 121 130 /// When unset, all authenticated endpoints will reject requests with HTTP 401. 131 + /// 132 + /// Authenticated requests always expect the username `admin` as per specification. 122 133 #[arg( 123 - id = "SA_SERVER_AUTH_TOKEN", 124 - long = "server-auth-token", 125 - env = "PORXIE_SERVER_AUTH_TOKEN" 134 + id = "SA_SERVER_ADMIN_PASSWORD", 135 + long = "server-admin-password", 136 + env = "PORXIE_SERVER_ADMIN_TOKEN" 126 137 )] 127 - auth_token: Option<String>, 138 + admin_password: Option<String>, 128 139 } 129 140 130 141 #[derive(Args)] ··· 350 361 /// 351 362 /// As pipes are used as a delimiter, they cannot be contained in headers. 352 363 /// 353 - /// Example (cli): '--policy-request-headers "Authorization: Bearer token" --policy-request-headers "X-Api-Key: your-key"' 364 + /// Example (cli): '--policy-request-headers "X-Hello: world" --policy-request-headers "X-Foo: bar"' 354 365 /// 355 - /// Example (env): 'PORXIE_POLICY_REQUEST_HEADERS="Authorization: Bearer token|X-Api-Key: your-key"' 366 + /// Example (env): 'PORXIE_POLICY_REQUEST_HEADERS="X-Hello: world|X-Foo: bar"' 356 367 #[arg( 357 368 id = "PA_POLICY_REQ_HEADERS", 358 369 long = "policy-request-headers", ··· 407 418 408 419 struct AppState { 409 420 // Authentication. 410 - auth_token: Option<String>, 421 + admin_password: Option<String>, 411 422 // Blob handling. 412 423 allowed_mimetypes: Vec<Mime>, 413 424 blob_service: BlobService, ··· 461 472 ownership_cache_ttl: args.cache.ownership_ttl.into(), 462 473 })?, 463 474 464 - auth_token: args.server.auth_token, 475 + admin_password: args.server.admin_password, 465 476 allowed_mimetypes: args.blob.allowed_mimetypes, 466 477 max_blob_size: args.blob.max_size, 467 478 cache_control_header: args.blob.cache_header, ··· 472 483 // Setup router. 473 484 let router = Router::new() 474 485 .route("/", get(get_index_handler)) 475 - .route("/health", get(get_health_handler)) 476 486 .route( 477 487 "/{did}/{cid}", 478 488 get(get_blob_handler).layer(TimeoutLayer::with_status_code( ··· 480 490 args.blob.processing_timeout.into(), 481 491 )), 482 492 ) 483 - .route("/cache/{id}", delete(delete_cache_handler)) 493 + .nest( 494 + "/xrpc", 495 + Router::new() 496 + .route("/_health", get(get_health_handler)) 497 + .route( 498 + "/net.dollware.porxie.getBlob", 499 + get(get_blob_handler_xrpc_compat).layer(TimeoutLayer::with_status_code( 500 + StatusCode::REQUEST_TIMEOUT, 501 + args.blob.processing_timeout.into(), 502 + )), 503 + ) 504 + .route( 505 + "/net.dollware.porxie.clearActorCache", 506 + post(clear_actor_cache_handler), 507 + ) 508 + .route( 509 + "/net.dollware.porxie.clearBlobCache", 510 + post(clear_blob_cache_handler), 511 + ), 512 + ) 484 513 .layer( 485 514 TraceLayer::new_for_http() 486 515 .make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO))
src/mime.rs crates/porxie/src/mime.rs
src/policy_client.rs crates/porxie/src/policy_client.rs
+2 -1
src/routes/blob/get.rs crates/porxie/src/routes/blob.rs
··· 223 223 ) 224 224 .header( 225 225 header::CONTENT_DISPOSITION, 226 - const { HeaderValue::from_static("attachment") }, 226 + HeaderValue::from_str(&format!(r#"attachment, filename="{cid}""#)) 227 + .unwrap_or(const { HeaderValue::from_static("attachment") }), 227 228 ) 228 229 .body(Body::from(blob.bytes)) 229 230 .expect("response should always build successfully"))
-3
src/routes/blob/mod.rs
··· 1 - mod get; 2 - 3 - pub use get::get_blob_handler;
-93
src/routes/cache/delete.rs
··· 1 - use crate::{ 2 - AppState, 3 - routes::{CACHE_CONTROL_NOCACHE_VALUE, ErrorResponse}, 4 - types::blob_cid::BlobCid, 5 - }; 6 - use axum::{ 7 - Json, 8 - extract::{Path, State}, 9 - http::{HeaderName, StatusCode, header}, 10 - }; 11 - use axum_extra::{ 12 - TypedHeader, 13 - headers::{Authorization, authorization::Bearer}, 14 - }; 15 - use jacquard_common::types::did::Did; 16 - use std::sync::Arc; 17 - use subtle::ConstantTimeEq; 18 - 19 - pub async fn delete_cache_handler( 20 - Path(identifier): Path<String>, 21 - State(state): State<Arc<AppState>>, 22 - TypedHeader(Authorization(bearer)): TypedHeader<Authorization<Bearer>>, 23 - ) -> Result< 24 - StatusCode, 25 - ( 26 - StatusCode, 27 - [(HeaderName, &'static str); 1], 28 - Json<ErrorResponse>, 29 - ), 30 - > { 31 - if !state 32 - .auth_token 33 - .as_ref() 34 - .map(|expected| expected.as_bytes().ct_eq(bearer.token().as_bytes()).into()) 35 - .unwrap_or(false) 36 - { 37 - return Err(( 38 - StatusCode::UNAUTHORIZED, 39 - [(header::CACHE_CONTROL, CACHE_CONTROL_NOCACHE_VALUE)], 40 - Json(ErrorResponse { 41 - error: "Unauthorized", 42 - message: None, 43 - }), 44 - )); 45 - } 46 - 47 - // TODO: Really need to expose a nicer cache purging API, 48 - // matching on prefix sucks. 49 - if identifier.starts_with("did:") { 50 - tracing::info!("invalidating DID cache entries"); 51 - let did = Did::new_owned(identifier).map_err(|_| { 52 - ( 53 - StatusCode::UNPROCESSABLE_ENTITY, 54 - [(header::CACHE_CONTROL, CACHE_CONTROL_NOCACHE_VALUE)], 55 - Json(ErrorResponse { 56 - error: "MalformedDid", 57 - message: Some("Invalid or unprocessable DID"), 58 - }), 59 - ) 60 - })?; 61 - state.identity_service.invalidate_did_cache(&did).await; 62 - if let Some(ref policy_client) = state.policy_client { 63 - policy_client.invalidate_policies({ 64 - let did = did.clone(); 65 - move |k, _v| k.0 == did 66 - }) 67 - } 68 - state 69 - .blob_service 70 - .invalidate_blob_ownership(move |k, _v| k.1 == did); 71 - } else { 72 - tracing::info!("invalidating CID cache entries"); 73 - let cid = BlobCid::try_from(identifier.as_str()).map_err(|_| { 74 - ( 75 - StatusCode::UNPROCESSABLE_ENTITY, 76 - [(header::CACHE_CONTROL, CACHE_CONTROL_NOCACHE_VALUE)], 77 - Json(ErrorResponse { 78 - error: "MalformedCid", 79 - message: Some("Invalid or unprocessable CID"), 80 - }), 81 - ) 82 - })?; 83 - state.blob_service.invalidate_blob(&cid).await; 84 - state 85 - .blob_service 86 - .invalidate_blob_ownership(move |k, _v| k.0 == cid); 87 - if let Some(ref policy_client) = state.policy_client { 88 - policy_client.invalidate_policies(move |k, _v| k.1 == cid) 89 - } 90 - } 91 - 92 - Ok(StatusCode::OK) 93 - }
-3
src/routes/cache/mod.rs
··· 1 - mod delete; 2 - 3 - pub use delete::*;
src/routes/health/get.rs crates/porxie/src/routes/xrpc/health.rs
-3
src/routes/health/mod.rs
··· 1 - pub mod get; 2 - 3 - pub use get::get_health_handler;
-38
src/routes/mod.rs
··· 1 - mod blob; 2 - mod cache; 3 - mod health; 4 - 5 - pub use blob::get_blob_handler; 6 - pub use cache::delete_cache_handler; 7 - pub use health::get_health_handler; 8 - 9 - /// A header value for [`header::CACHE_CONTROL`] indicating the response cannot be cached at all. 10 - pub const CACHE_CONTROL_NOCACHE_VALUE: &str = "must-understand, no-store"; 11 - 12 - #[derive(serde::Serialize)] 13 - pub struct ErrorResponse { 14 - error: &'static str, 15 - message: Option<&'static str>, 16 - } 17 - 18 - pub async fn get_index_handler() -> &'static str { 19 - r#" 20 - _____ _ 21 - | __ \ (_) 22 - | |__) |__ _ ____ ___ ___ 23 - | ___/ _ \| '__\ \/ / |/ _ \ 24 - | | | (_) | | > <| | __/ 25 - |_| \___/|_| /_/\_\_|\___| 26 - 27 - 28 - A correct and efficient ATProto blob proxy for secure content delivery. 29 - 30 - Links: 31 - - Repo: https://codeberg.org/Blooym/porxie 32 - - ATProto: https://atproto.com 33 - 34 - Routes: 35 - - HTTP GET /{did}/{cid} - Resolve and fetch a blob from its origin. 36 - - HTTP DELETE /cache/{cid or did} - Invalidate cache for either a CID (blob, policy, ownership) or for a DID (ownerships and policies). Requires auth. 37 - "# 38 - }
src/types/blob_cid.rs crates/porxie/src/types/blob_cid.rs
src/types/mod.rs crates/porxie/src/types/mod.rs