Files
libnovel/docs/d2/architecture.svg
Admin 040072c3f5
All checks were successful
CI / Backend (pull_request) Successful in 33s
CI / UI (pull_request) Successful in 40s
docs(d2): update architecture and api-routing diagrams to current state
architecture.d2:
- Split app into prod VPS (165.22.70.138) and homelab runner (192.168.0.109)
- Add CrowdSec, Dozzle agent, pocket-tts (voice samples)
- Valkey now shown as Asynq job queue in addition to presign cache
- Add caddy-l4 Redis TCP proxy (:6380) to Caddy label
- Add CI/CD node (Gitea Actions) with full job list incl. releases.json bake
- Remove runner from prod app group (it runs on homelab only)
- Watchtower: note runner is label-disabled on prod

api-routing.d2:
- Add /api/presign/* routes to backend (presign_be group)
- Add /api/audio POST + status GET to both sk and be
- Add /api/scrape/book and /api/scrape/book/range to scrape_sk
- Catalogue: annotate Meilisearch vs legacy browse
- Add Meilisearch filter/sort fields to storage node
- Add Asynq queue note to Valkey storage node
- Fix presign proxy: sk routes through be.presign_be, not directly to storage
2026-03-28 22:44:31 +05:00

133 lines
65 KiB
XML

<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-d2-version="0.7.1" preserveAspectRatio="xMinYMin meet" viewBox="0 0 4010 2454"><svg class="d2-286243964 d2-svg" width="4010" height="2454" viewBox="-9 -101 4010 2454"><rect x="-9.000000" y="-101.000000" width="4010.000000" height="2454.000000" rx="0.000000" fill="#FFFFFF" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
.d2-286243964 .text {
font-family: "d2-286243964-font-regular";
}
@font-face {
font-family: d2-286243964-font-regular;
src: url("data:application/font-woff;base64,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");
}
.d2-286243964 .text-bold {
font-family: "d2-286243964-font-bold";
}
@font-face {
font-family: d2-286243964-font-bold;
src: url("data:application/font-woff;base64,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");
}
.d2-286243964 .text-italic {
font-family: "d2-286243964-font-italic";
}
@font-face {
font-family: d2-286243964-font-italic;
src: url("data:application/font-woff;base64,d09GRgABAAAAABg4AAoAAAAAJFgAARhRAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAAA9AAAAGAAAABgW1SVeGNtYXAAAAFUAAAA4QAAAUotICkuZ2x5ZgAAAjgAABCJAAAXWJc1mdNoZWFkAAASxAAAADYAAAA2G7Ur2mhoZWEAABL8AAAAJAAAACQLeAjyaG10eAAAEyAAAAEOAAABOI1rCJZsb2NhAAAUMAAAAJ4AAACe8Urrtm1heHAAABTQAAAAIAAAACAAZgD2bmFtZQAAFPAAAAMmAAAIMgntVzNwb3N0AAAYGAAAACAAAAAg/8YAMgADAeEBkAAFAAACigJY//EASwKKAlgARAFeADIBIwAAAgsFAwMEAwkCBCAAAHcAAAADAAAAAAAAAABBREJPAAEAIP//Au7/BgAAA9gBESAAAZMAAAAAAeYClAAAACAAA3icjM9JTppxAIbx31conWhpCy04fziggDiPqCjOujJuXBKv4cILuPMyuNS1dzCaeI+/kRDd+q6f5H0eRBIipCWjBkpiSSmxknFlVTVTpq2qO3Gm5cKldpwrXodAl5tQ6XIz6ppOtZy/c+FJWkG/AVkZeQnJcBduw3Noh/tw9Vh7uOlYfGSROZt27TlwaMuGffMWLFqybKVjumZdw6fXH5+lfPHVN9/9kPbTLxm//fFXVs4//+XtKOjRq69jOWhIrGjYiFFj3cayiqrJt9JZR7Y1HfMCAAD//wEAAP//8bQpgQAAAHicfHh7dBvlmf73fSNrfJFly7pZimVZGmlGl5FkzUgaybIky7JlWxdf4wuJb7nHiUlMwEloEkoJP34hu6SCky0HDm3Ywva05bTkhHbPtqW0kG4xsOHs7qG7UCC73EybbA/g46W0B4/2zIxsy9nLP9J3pPne73uf93mf930HVAAbAOgIuggwUAXqQAPQAsCqLRjGchyhx1iKInCco9Rq3HYfXLrvcVlqx0eOJ/9Em2U9X/te9j9mn0EX1+bhvVNf/Sq/89y+feM3b/Iu+C83AQAAFV8DAP4GFUAVUAGgxlmKJClCLoeQVRMUgX/QdrVaVi2TGVn+H+DeHbmhht/NwbsXFgKHwpED/BAqrC1cuwYABggAUAsqABUwCmtWzTI6rUYux3Gd+E1gLBMKBkhic0GcfXZm3p2yQTbdc7q/bXp6R3dm5+Fj00fyfXehQqaH7qIrZYpkuG+Khsd7OA+zdqM7x8SEe0MQKa4iD3oCmAGosJJkMBBHLKPT4yRJWJVIq9HpWCbE6eVyaM0eDLXuOJMLDzWG1CGybabTZs1EHakWwjalSJ3sz1880cO5nC1UbO/J9uhUsGUbY/YI2Ig+hURs1GUeERTLhNY9+MqDD41eOjo2Nno6dWBPCBX+/90nfrSvY/s3dk3NSfcUbNSjAqgRY4ZbcBYncAtOnIWHavkPXJ8pP2EhqUSF5G86P+8se76q7Hms9LTns9pP21Eh+WEn/6/rGETRE8AqYvC/QMARHIvJ5ZA+fqZ159eGokMGTs054ru7bUQuYYuo7edqX4/YphUPn+y/eCK9AUTbdKix/scd/IfN9g0/5tb9YDGLmsUItQUjzvaHoSOcP9uf4N+MowJ/E2rXFmCYX5L2gFVUAJi0hzjbf1YgjGCvyBdX4RIqgDoA9FaS4qTLqoWrXmob1rLKiJOJ1072T9fvU3Bet82ZTcJrvGP0aOku46gAFJJdFrK4msBwnDjbn8Rg38TnfzV0z4MeVOCfh11f8vNw9wPvrPsAH0EFUFHyQdhwHGpqUWHtyjr2z6MCMIj/q/UsJ3oZCnEEjhGYkBc4Rpydiuhk6atTZ7O5KqNCNvAiHdPJ5MrKDCrw3zp3Du5eW4DH6EPuR/in4eQj9BzNXyjZ3o8KJSap9WxI9HbTav83XDK5sro7ezZ/0S2T11WnUYGffNB/Owsn1xbgUw+xhxj+ksjL9uIqmkZPgHrQUh55nVajRBQTRwI3JQZA85FF79hiOrMv4B27KxUcj1sz/cJnn+LR09nCYnfXqZHsw4vdqfbdi5Fdi9Hdi22zxze47xHjrSnnPoGpN9P3J5PHMl/bPhdIzuw7lOvdhwqZscEDfv4L2DM4EGHBhh0KFUAt0G3aEcK1xdKPJu84MnLnyPwxrmvP9N5s7ywqpEd2HlHxH0AdfwOODqdDPimfFMVVyKMngKucN8EASVGCCIRCGxkgl2s1Or1eUp+PUwuOiGmUax/y2HOuaHAyGp01s4a01x40+W05XyC6X9HW5nYzXWEbo/Ma+zhmmAk4vM1Oc+s20qfzNPVwbTsDAIIpAFAQFQAueENwFpzAvrP4Qi18rfYXiyifSq09J91zoLgqxltXipDEba1mk+awee9huSzTn63q6A7v0A7lhpvuU8zt1/oMcIF/0GNN5ycPw0f4wxfuFuwNA4CSIo6AxVi1TldiEHwoOrCtohKTGYLGH2/nv4cK/MXg7aHg0QCcF1MNQDADAEajArBIvJfLcWmvWqfRlqwQWCDEiUycqRqsxjCZTN+qe7qnCso0bs2FPL+yB0dQprSonkMF/tHAfDA4H4Bz/KOBw6HQ4QCcW1uAD9sGKCpH8UfFuFPFVfgFXAEagQH6TXViORYjhJMoQZs2pOq5jhydmWapmEqmju9KVMqIiQZywEZrmSZbKmj2K3aOpu+eZB2WGG/stfs6vL63SKurb4pJxCSemYur8FO0BLRCtRSYQeCEmsUFT0XOlWWGWJNuUDEVpklcyFM6ZNvuEY8P2lLB5landYjwaliFwxJDSy/Mmtw7xoSjO1x9U2w85rJ/TFoBBPbiKrwCV0DTFu82mVeqPm8O7KXzu4J0u86jJk2tY6FIW0tIZzXmFfunuu4c9VkNrXpt10KqM21UMRo7WMcOUWW+bGL3f4PX1oDVk/lCCb1++63oUS0zL6yFb4UPib78Aq4AI7CXnydmqkW+UUkxVix7gocfjs15spOtXLJZUcH/qqol5TJF9M2moceKCGtwEsFpxaFd3QvDtHeQaWKViUG7QcVqzdBe01jb5DePAgjcAMCH0BtAL+ZRApVnLi4WPfdooiZZX9cfM7oatlVvU1mclardij2j8LuRiqHMSG0Nh1cz7pE4PyFgBos2uAJXgBl4t1QUTi4ntrJPLse2oPeMf4ywNXU74hmlgdzuiw26+yb9ZFyFqRP71XdGiCGrW+dvIpJss+8d0hTUW3MdB0l6bDR1122MwEdsZj+0uF3/SFqd6YnWaFTKOTMA8E20VKolmzzExYISDAhuYuYL+dZ6mXOYjgcr47l2may3qdfbjZZuxghfMmy28a9CWtNYm3V5+e8Wi4JN8Gd0BZEgCwCQg1wvAKBYLN5fpMAfxd/z0u/dm3f4A1raqJNqoU5SOG6+kJ9Ff5p4abF/asGIlngThK/xH/3h2CkAAV1cBX9GS6BBQDEYkKRKqylR4Pak/FT+DIQqTI7Dap0ioTKgw2sP41VYA0RRmWzjXHQDrggaLZwpua4vASDfgkA5GLsSuIwcIdv8Fb4Jeywkk8XzMZmsR9tLdwvYpHW97m643Gfzcw6aTYZVzZpyfDZXm/jDFdBYfodb4RdOdA57t6AvnnAr+Bt5Cd+GK6AOmMrzRBIXqSWUkv+NgWk6M80MzNDZaZdniA0xwofi4M7uO0e90mdH50JXZ09qoaszLfbhnxdZ+ClckXIeL7uxEhGimuHqLfpVfT4hx+yjXjH1GbJdjRrMf1OuX9fQcx1mTynxzQcvQVgSMPJ3dsu6P6yo0eKZFZwgLLfkytZMgRZLM7JPeMu1+vylcqG5dukE6duQ6rU8hFuFWorLabgC6sviosfJ9XjUyEw5j0G7rd5oy5ljcHmKjlV1VSai/DUAi18WV+EZuAKoW2v/raVfqPxS4X/KP2Vo1XeQrpgz7I3QfbQ30+RVsxbSH2qJB1qHFQEHaXZ4CSNlNsad7qTd1uzQGD3mZrLB2k57uuzCnduLq3ACzW9ofYgTFIsVVapM63/SEZDBSE9NzpbcdkpxJoI1WZXGGlW9T5Hw1BlrYUOk4oEH4vyNhobm5uoKDq8TbIeLq/ATuCzohH6zTyhlnLok989sZEOvqYfuzgkF0rFd0cmpzGoY4t9QGwSawgnemCFYKQejAMD34PJ/7xfu68nZZHKZTGVTfz3Pr8Fl/mMiS9j6bNDAG6W9aQDQy3BZ7BfK926uMAKT5kQcmyNy9RBCWd22+nuzKiR0Ccb6r/a+O6MUfzXVHYfL/PvWLqu1ywqby1ZGWE302my9BP85gMU3AID/LOFAqClWXzqKY3E9UZpJcZz+7c5+V6USl9W11I2OLO0ZoCtV1bJ6q3oaog/ndZRW49TO/+dnx3RenY7W3ynMGS8VffADuAyMAOAiZ8SisAURJZJXtygNDQ32pKFhJEcK3ZTK3vCXOf59Q7T3n3A8UhVjCPgx/4klTxA5K1StfebL0yJWxc8BgD+Ey8K8RnBQaAghi1dXwtS7tTBWyf+cV9DwdNzD/7+48DwG6oqrqAs9AWqAH8QAUGv061mNieEPcetcFqdnnaCbHCus9DhJyeWURDdSWH9UQyaoRqO7zZ/Z6cu5G1r7vaFQaLtXm7Q7Y8a0OdfK+Vpicdfs1yNeoruF6tR7k/DfSVrvDRN6S3btrZF0aDBujIbC4/6Yl83HTYE9Dvfets5TAVoV1wSt1FPecJPBfZAz9wl64SquwtfReaASmKHfnLr/h8Fjiemxu/pmg0za5uyb8VOpgIn2ip+K8J74bX99uqdtT3zHk6fSsa47znWlxrvvONfVOQ6gECd4L7pHnDM5ocMOcSzG4sbav5i9o3qUi951n6IDXmcU1rWXOtbx/xU6L+wjuDhWEkhqQzxxC15dOXth2scGW5JWih5vHZ5wDZ8egRqFd+jU7tu8dLvF3Eo6b+sKTs8u9HYKNv9YXIWvoPPAcYvOENyGyuPUejXTSkLzs+S+Zlaf8XeNb9+nGNhJMawpZaJGpgbHs5lgNDanSHoc1kA2wna2OWPNrlCTnk0MdsYmtTJVLxO7zS/gKwjCNXQPqBZmLgvBWTgo+E7YWS4kdecw20vwv6+C09sHRxQjfPHvSXkDLtM4NJcD8HF+IR7/uSlpaQo0SnkMhHqF7hGmxE0/NhxQW3AC32Da88lpE6NLhl29dCJgplssg9Bd+/uAymXonUkdUSQ8TkvAlWfj7fUqI/R0Pl+pGB3JHY2JdYQtrsKb6DyoAzQAnKb8FLlGv+XtjFAgNw89FWXIdoJlDAM2OBcadHsGb+8IdmsC1nZmPKG0bLf0jHIzr3aP+jIOLmn11ejfD+9K7H7qRKe/xdmWOrWdtE30xw8JPABzAKAKdI/Y68QRZ+EsuBLhJ02Zo0P8q0p4oXrP8ZT+5Ms/GOxkpp7/9e1iLlqKq/C36DwwAzeIrDM6FOKCG/VEinAzEi6rXndKq9FhkodCEmo1uuve8bC7i2pqDowzzj5vmtM4mtp3meztYRedjjfbOhzOFMV09ilsfWF/JqiSNUUpLu9qSTIdY2ZZrTNsbRvxwH2NGcYXiAaZKP9TU9hhZ53apmyYK8039cVV+PI6xmpJBEKBUsuhXle20Hq7u357JXoowDQOEjDK2Ntt9YFuTaAlzowlai0jlvQoNx3lBgTU4Q8kfBUC1ry51SLAO2wnJvoTcx3c7sSup090SpzSF1fBOTAv5KdUr6RQpnUGqknXaFc06Yy0SWegQbEoPrsE30UUiIEOeAeQA6kHuBd+H3yGLoEKANSCwuP36uuyahf8/mOTk4+J70quw2poABgAwt+E4u3a6+t9NQHeQ9ehXvrPghM16HLNe6GQ0AeDAagAv4T3IxzEQBg2ivr8TvF+eLn4t8LzOGfBbTXwlerTDCPaShYH4Th6W3wHVQq9Xi6+ONN/pdHCHcx4Ds1XaZSXO54aXnzlZ1OGB/h/+5Z3/ywp2H2jOAhulPZSoQaRDJwkBdBz6HBVQx0jmLhsfABavunbP0OqO749vPjqT4W9f1echd9GvxbvBFnYC6+E+fyT2P4vH5d8PAO/A7+JXgFKANQUR3F6To9zelyPU5dbuica9hjoygP4AdIRgFdME36H5ZDssNJt3qWfABC0ggPwVeQS4sMFiSAbZLWsltBe/+Gz7c9ePvBy5OrVyMvCGR4og1fhI8L8Ba1etCGazWj9vWlum5ciNTpC5TYkzNLaVu82JKDMaDWaWpp8LunbK4xyQFP8FMVRhVAHKyjOErRQHNTCMHWV74C/vEpBeb3rxcSLLv6Lio0ZAVyDy+vvBs278rvhstiAQNCDsuAKuiL4oC7j2El1M6HXmAiU1esMlkadoQVAcfZ5HS4LccBveY/o1xMqQ7WmvslSfTR/VJl6q7oqIsf9bmRbezc99l8AAAD//wEAAP//SqfttwAAAAABAAAAARhRBkoMl18PPPUAAQPoAAAAANhdoMwAAAAA3WYvN/69/t0IHQPJAAIAAwACAAAAAAAAAAEAAAPY/u8AAAhA/r39vAgdA+gAwv/RAAAAAAAAAAAAAABOeJwszjFLKlAYxvH/8zree7nCvWC5HOJNhSwKWpJcGtIlsCLaorUImloagoY+QW19h6aWRqtFCJpcAocTzUVIaIR4Qml4lgf+8LMjpmmBhunB6rgVqdg6rj6uJ9zWqNghzgBXKw1tHtcjbre4HeB2TNWKuJVxffFbQ3Zsj4be2bQPdm2Skk0QdE3BcpT0TEF5yvYP2S8CrwTe0pk6BD6ZzQSC/SFYhpLlUm/Ua5ug8zTQKlXLsqQmy3ZPTVeprWa602XqWY2/iswokldMPcXUVxx/KLKoyL4iU4pk6ZL72anKeEY4XRp6SR05KyqktrbSjS44UWRBkTnb4P/YCvWR9xsAAP//AQAA//9YHFFHAAAAAAAuAC4AUgCKALwA3gD2AQwBQgFcAWoBiAGkAbQB4gIIAjoCXgKGAsYC2gMCAyADWgOSA8oD+AQwBGoEkgTaBQQFEAUcBTYFWAWaBcQF8gYsBmYGhAbABu4HGgc4B3IHngfOB+YIRAh2CI4IuAj0CRwJUAmSCawKAgpGClwKaAp4CoYKlAqeCqwKygroCvgLFgsqC1ILagt4C44LrAAAAAEAAABOAIwADABmAAcAAQAAAAAAAAAAAAAAAAAEAAN4nJyU204bVxSGPwfbbXq6qFBEbtC+TKVkTKMQJeHKlKCMinDqcXqQqkqDPT6I8czIM5iSJ+h136Jvkas+Rp+i6nW1fy+DHUVBIAT8e/Y6/Gutf21gk//YoFa/C/zdnBuusd382fAdvmgeGd5gv/mZ4ToPG/8YbjBovDXc5EGja/gT3tX/NPwpT+q/Gb7LVv3Q8Oc8rm8a/nLD8a/hr3jCuwWuwTP+MFxji8LwHTb51fAG97CYtTr32DHc4Gu2DTfZBnpMqEiZkDHCMWTCiDNmJJREJMyYMCRhgCOkTUqlrxmxkGP0wa8xERUzYkUcU+FIiUiJKRlbxLfyynmtjEOdZnbXpmJMzIk8TonJcOSMyMlIOFWcioqCF7RoUdIX34KKkoCSCSkBOTNGtOhwyBE9xkwocRwqkmcWkTOk4pxY+Z1Z+M70ScgojdUZGQPxdOKXyDvkCEeHQrarkY/WIjzE8aO8Pbdctt8S6NetMFvPu2QTM1c/U3Ul1c25JjjWrc/b5gfhihe4W/Vnncn1PRrof6XIJ5xp/gNNKhOTDOe2aBNJQZG7j2Nf55BIHfmJkB6v6PCGns5tunRpc0yPkJfy7dDF8R0djjmQRyi8uDuUYo75Bcf3hLLxsRPrz2JiCb9TmLpLcZypjimFeu6ZB6o1UYU3n7DfoXxNHaV8+tojb+k0v0x7FjMyVRRiOFUvl9oorX8DU8RUtfjZXt37bZjb7i23+IJcO+zVuuDkJ7dgdN1Ug/c0c66fgJgBOSey6JMzpUXFhXi/JuaMFMeBuvdKW1LRvvTxeS6kkoSpGIRkijOj0N/YdBMZ9/6a7p29JQP5e6anl1XdJotTr65m9EbdW95F1uVkZQItm2q+oqa+uGam/UQ7tco/km+p1y3nEaHiLnb7Q6/ADs/ZZY+xsvR1M7+886+Et9hTB05JZDWUpn0NjwnYJeApu+zynKfv9XLJxhkft8ZnNX+bA/bpsHdtNQvbDvu8XIv28cx/ie2O6nE8ujw9u/U0H9xAtd9o367eza4m56cxt2hX23FMzNRzcVurNbn7BP8DAAD//wEAAP//cqFRQAAAAAMAAP/1AAD/zgAyAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
}]]></style><style type="text/css"><![CDATA[.shape {
shape-rendering: geometricPrecision;
stroke-linejoin: round;
}
.connection {
stroke-linecap: round;
stroke-linejoin: round;
}
.blend {
mix-blend-mode: multiply;
opacity: 0.5;
}
.d2-286243964 .fill-N1{fill:#0A0F25;}
.d2-286243964 .fill-N2{fill:#676C7E;}
.d2-286243964 .fill-N3{fill:#9499AB;}
.d2-286243964 .fill-N4{fill:#CFD2DD;}
.d2-286243964 .fill-N5{fill:#DEE1EB;}
.d2-286243964 .fill-N6{fill:#EEF1F8;}
.d2-286243964 .fill-N7{fill:#FFFFFF;}
.d2-286243964 .fill-B1{fill:#0D32B2;}
.d2-286243964 .fill-B2{fill:#0D32B2;}
.d2-286243964 .fill-B3{fill:#E3E9FD;}
.d2-286243964 .fill-B4{fill:#E3E9FD;}
.d2-286243964 .fill-B5{fill:#EDF0FD;}
.d2-286243964 .fill-B6{fill:#F7F8FE;}
.d2-286243964 .fill-AA2{fill:#4A6FF3;}
.d2-286243964 .fill-AA4{fill:#EDF0FD;}
.d2-286243964 .fill-AA5{fill:#F7F8FE;}
.d2-286243964 .fill-AB4{fill:#EDF0FD;}
.d2-286243964 .fill-AB5{fill:#F7F8FE;}
.d2-286243964 .stroke-N1{stroke:#0A0F25;}
.d2-286243964 .stroke-N2{stroke:#676C7E;}
.d2-286243964 .stroke-N3{stroke:#9499AB;}
.d2-286243964 .stroke-N4{stroke:#CFD2DD;}
.d2-286243964 .stroke-N5{stroke:#DEE1EB;}
.d2-286243964 .stroke-N6{stroke:#EEF1F8;}
.d2-286243964 .stroke-N7{stroke:#FFFFFF;}
.d2-286243964 .stroke-B1{stroke:#0D32B2;}
.d2-286243964 .stroke-B2{stroke:#0D32B2;}
.d2-286243964 .stroke-B3{stroke:#E3E9FD;}
.d2-286243964 .stroke-B4{stroke:#E3E9FD;}
.d2-286243964 .stroke-B5{stroke:#EDF0FD;}
.d2-286243964 .stroke-B6{stroke:#F7F8FE;}
.d2-286243964 .stroke-AA2{stroke:#4A6FF3;}
.d2-286243964 .stroke-AA4{stroke:#EDF0FD;}
.d2-286243964 .stroke-AA5{stroke:#F7F8FE;}
.d2-286243964 .stroke-AB4{stroke:#EDF0FD;}
.d2-286243964 .stroke-AB5{stroke:#F7F8FE;}
.d2-286243964 .background-color-N1{background-color:#0A0F25;}
.d2-286243964 .background-color-N2{background-color:#676C7E;}
.d2-286243964 .background-color-N3{background-color:#9499AB;}
.d2-286243964 .background-color-N4{background-color:#CFD2DD;}
.d2-286243964 .background-color-N5{background-color:#DEE1EB;}
.d2-286243964 .background-color-N6{background-color:#EEF1F8;}
.d2-286243964 .background-color-N7{background-color:#FFFFFF;}
.d2-286243964 .background-color-B1{background-color:#0D32B2;}
.d2-286243964 .background-color-B2{background-color:#0D32B2;}
.d2-286243964 .background-color-B3{background-color:#E3E9FD;}
.d2-286243964 .background-color-B4{background-color:#E3E9FD;}
.d2-286243964 .background-color-B5{background-color:#EDF0FD;}
.d2-286243964 .background-color-B6{background-color:#F7F8FE;}
.d2-286243964 .background-color-AA2{background-color:#4A6FF3;}
.d2-286243964 .background-color-AA4{background-color:#EDF0FD;}
.d2-286243964 .background-color-AA5{background-color:#F7F8FE;}
.d2-286243964 .background-color-AB4{background-color:#EDF0FD;}
.d2-286243964 .background-color-AB5{background-color:#F7F8FE;}
.d2-286243964 .color-N1{color:#0A0F25;}
.d2-286243964 .color-N2{color:#676C7E;}
.d2-286243964 .color-N3{color:#9499AB;}
.d2-286243964 .color-N4{color:#CFD2DD;}
.d2-286243964 .color-N5{color:#DEE1EB;}
.d2-286243964 .color-N6{color:#EEF1F8;}
.d2-286243964 .color-N7{color:#FFFFFF;}
.d2-286243964 .color-B1{color:#0D32B2;}
.d2-286243964 .color-B2{color:#0D32B2;}
.d2-286243964 .color-B3{color:#E3E9FD;}
.d2-286243964 .color-B4{color:#E3E9FD;}
.d2-286243964 .color-B5{color:#EDF0FD;}
.d2-286243964 .color-B6{color:#F7F8FE;}
.d2-286243964 .color-AA2{color:#4A6FF3;}
.d2-286243964 .color-AA4{color:#EDF0FD;}
.d2-286243964 .color-AA5{color:#F7F8FE;}
.d2-286243964 .color-AB4{color:#EDF0FD;}
.d2-286243964 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker-d2-286243964);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker-d2-286243964);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright-d2-286243964);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright-d2-286243964);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright-d2-286243964);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright-d2-286243964);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark-d2-286243964);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright-d2-286243964);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright-d2-286243964);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright-d2-286243964);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright-d2-286243964);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker-d2-286243964);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark-d2-286243964);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal-d2-286243964);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal-d2-286243964);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright-d2-286243964);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright-d2-286243964);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright-d2-286243964);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]></style><g class="bm92ZWxmaXJl"><g class="shape" ><path d="M 3670 2178 C 3670 2179 3669 2180 3668 2180 C 3652 2182 3640 2197 3640 2216 C 3640 2236 3654 2252 3672 2252 H 3792 C 3811 2252 3826 2235 3826 2214 C 3826 2194 3812 2177 3794 2176 C 3793 2176 3792 2175 3791 2174 C 3787 2154 3768 2139 3747 2139 C 3733 2139 3720 2145 3712 2155 C 3711 2156 3710 2157 3709 2156 C 3706 2155 3702 2154 3699 2154 C 3683 2154 3671 2164 3670 2178 Z" stroke="#0D32B2" fill="#f0f4ff" class=" stroke-B1" style="stroke-width:2;" /></g><text x="3731.167000" y="2213.736000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="3731.167000" dy="0.000000">novelfire.net</tspan><tspan x="3731.167000" dy="18.500000">(scrape source)</tspan></text></g><g class="a29rb3Jv"><g class="shape" ><path d="M 3646 1985 C 3646 1987 3644 1988 3643 1988 C 3621 1990 3604 2009 3604 2033 C 3604 2058 3623 2078 3647 2078 H 3812 C 3838 2078 3859 2056 3859 2030 C 3859 2005 3839 1984 3815 1983 C 3813 1983 3812 1982 3811 1980 C 3805 1955 3780 1936 3750 1936 C 3730 1936 3713 1944 3702 1957 C 3701 1959 3699 1959 3697 1959 C 3693 1957 3688 1957 3683 1957 C 3664 1955 3648 1968 3646 1985 Z" stroke="#0D32B2" fill="#f0f4ff" class=" stroke-B1" style="stroke-width:2;" /></g><text x="3730.832000" y="2022.682000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="3730.832000" dy="0.000000">Kokoro-FastAPI TTS</tspan><tspan x="3730.832000" dy="17.666667">(self-hosted · homelab)</tspan><tspan x="3730.832000" dy="17.666667">chapter audio</tspan></text></g><g class="cG9ja2V0dHRz"><g class="shape" ><path d="M 3646 50 C 3646 52 3644 53 3643 53 C 3621 55 3604 74 3604 98 C 3604 123 3623 143 3647 143 H 3812 C 3838 143 3859 121 3859 95 C 3859 70 3839 49 3815 48 C 3813 48 3812 47 3811 45 C 3805 20 3780 1 3750 1 C 3730 1 3713 9 3702 22 C 3701 24 3699 24 3697 24 C 3693 22 3688 22 3683 22 C 3664 20 3648 33 3646 50 Z" stroke="#0D32B2" fill="#f0f4ff" class=" stroke-B1" style="stroke-width:2;" /></g><text x="3730.832000" y="87.682000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="3730.832000" dy="0.000000">pocket-tts</tspan><tspan x="3730.832000" dy="17.666667">(self-hosted · homelab)</tspan><tspan x="3730.832000" dy="17.666667">voice sample MP3s</tspan></text></g><g class="bGV0c2VuY3J5cHQ="><g class="shape" ><path d="M 1963 323 C 1963 324 1961 325 1960 325 C 1940 327 1925 342 1925 361 C 1925 381 1942 397 1964 397 H 2113 C 2136 397 2156 380 2156 359 C 2156 339 2138 322 2116 321 C 2114 321 2113 320 2113 319 C 2108 299 2085 284 2058 284 C 2040 284 2025 290 2015 300 C 2014 301 2012 302 2011 301 C 2007 300 2003 299 1998 299 C 1979 299 1965 309 1963 323 Z" stroke="#0D32B2" fill="#f0f4ff" class=" stroke-B1" style="stroke-width:2;" /></g><text x="2039.594500" y="358.736000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="2039.594500" dy="0.000000">Let&#39;s Encrypt</tspan><tspan x="2039.594500" dy="18.500000">(ACME TLS-ALPN-01)</tspan></text></g><g class="YnJvd3Nlcg=="><g class="shape" ><path d="M 328 757 H 188 V 756 C 188 740 203 726 228 719 C 214 713 206 704 206 694 C 206 678 229 664 258 664 C 286 664 310 678 310 694 C 310 704 302 713 289 719 C 313 726 329 740 329 756 V 757 H 328 Z" stroke="#0D32B2" fill="#fff9e6" class=" stroke-B1" style="stroke-width:2;" /></g><text x="258.000000" y="778.000000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Browser / iOS App</text></g><g class="aW5pdA=="><g class="shape" ><rect x="2661.000000" y="75.000000" width="267.000000" height="332.000000" stroke="#0D32B2" fill="#f5f5f5" class=" stroke-B2" style="stroke-width:2;stroke-dasharray:8.000000,7.892511;" /></g><text x="2794.500000" y="62.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:28px">Init containers</text></g><g class="c3RvcmFnZQ=="><g class="shape" ><rect x="3564.000000" y="454.000000" width="336.000000" height="1296.000000" stroke="#0D32B2" fill="#eaf7ea" class=" stroke-B1" style="stroke-width:2;" /></g><text x="3732.000000" y="441.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:28px">Storage</text></g><g class="YXBw"><g class="shape" ><rect x="1051.000000" y="529.000000" width="1905.000000" height="726.000000" stroke="#0D32B2" fill="#eef3ff" class=" stroke-B1" style="stroke-width:2;" /></g><text x="2003.500000" y="516.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:28px">Application — prod (165.22.70.138)</text></g><g class="aG9tZWxhYg=="><g class="shape" ><rect x="2655.000000" y="1774.000000" width="279.000000" height="222.000000" stroke="#0D32B2" fill="#fef9ec" class=" stroke-B1" style="stroke-width:2;" /></g><text x="2794.500000" y="1761.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:28px">Runner — homelab (192.168.0.109)</text></g><g class="b3Bz"><g class="shape" ><rect x="1066.000000" y="1729.000000" width="331.000000" height="206.000000" stroke="#0D32B2" fill="#f5f5f5" class=" stroke-B1" style="stroke-width:2;" /></g><text x="1231.500000" y="1716.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:28px">Ops</text></g><g class="Y2ljZA=="><g class="shape" ><rect x="92.000000" y="1689.000000" width="331.000000" height="286.000000" stroke="#0D32B2" fill="#f0f9ff" class=" stroke-B1" style="stroke-width:2;" /></g><text x="257.500000" y="1676.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:28px">CI / CD</text></g><g class="aW5pdC5taW5pby1pbml0"><g class="shape" ><rect x="2699.000000" y="105.000000" width="191.000000" height="114.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="2794.500000" y="143.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="2794.500000" dy="0.000000">minio-init</tspan><tspan x="2794.500000" dy="17.250000">(mc: create buckets</tspan><tspan x="2794.500000" dy="17.250000"> chapters · audio</tspan><tspan x="2794.500000" dy="17.250000"> avatars · catalogue)</tspan></text></g><g class="aW5pdC5wYi1pbml0"><g class="shape" ><rect x="2691.000000" y="279.000000" width="207.000000" height="98.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="2794.500000" y="317.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="2794.500000" dy="0.000000">pb-init</tspan><tspan x="2794.500000" dy="17.666667">(bootstrap PocketBase</tspan><tspan x="2794.500000" dy="17.666667"> collections + schema)</tspan></text></g><g class="c3RvcmFnZS5taW5pbw=="><g class="shape" ><path d="M 3639 838 C 3639 814 3723 814 3732 814 C 3741 814 3825 814 3825 838 V 956 C 3825 980 3741 980 3732 980 C 3723 980 3639 980 3639 956 V 838 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /><path d="M 3639 838 C 3639 862 3723 862 3732 862 C 3741 862 3825 862 3825 838" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="3732.000000" y="890.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="3732.000000" dy="0.000000">MinIO :9000</tspan><tspan x="3732.000000" dy="17.250000">buckets:</tspan><tspan x="3732.000000" dy="17.250000"> chapters · audio</tspan><tspan x="3732.000000" dy="17.250000"> avatars · catalogue</tspan></text></g><g class="c3RvcmFnZS5wb2NrZXRiYXNl"><g class="shape" ><path d="M 3622 508 C 3622 484 3721 484 3732 484 C 3743 484 3842 484 3842 508 V 674 C 3842 698 3743 698 3732 698 C 3721 698 3622 698 3622 674 V 508 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /><path d="M 3622 508 C 3622 532 3721 532 3732 532 C 3743 532 3842 532 3842 508" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="3732.000000" y="560.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="3732.000000" dy="0.000000">PocketBase :8090</tspan><tspan x="3732.000000" dy="16.714286">collections:</tspan><tspan x="3732.000000" dy="16.714286"> books · chapters_idx</tspan><tspan x="3732.000000" dy="16.714286"> audio_cache · progress</tspan><tspan x="3732.000000" dy="16.714286"> scrape_jobs · app_users</tspan><tspan x="3732.000000" dy="16.714286"> ranking · library</tspan><tspan x="3732.000000" dy="16.714286"> comments</tspan></text></g><g class="c3RvcmFnZS52YWxrZXk="><g class="shape" ><path d="M 3595 1594 C 3595 1570 3718 1570 3732 1570 C 3746 1570 3869 1570 3869 1594 V 1696 C 3869 1720 3746 1720 3732 1720 C 3718 1720 3595 1720 3595 1696 V 1594 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /><path d="M 3595 1594 C 3595 1618 3718 1618 3732 1618 C 3746 1618 3869 1618 3869 1594" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="3732.000000" y="1646.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="3732.000000" dy="0.000000">Valkey :6379</tspan><tspan x="3732.000000" dy="17.666667">presign URL cache (TTL ~55 min)</tspan><tspan x="3732.000000" dy="17.666667">Asynq job queue (runner tasks)</tspan></text></g><g class="c3RvcmFnZS5tZWlsaXNlYXJjaA=="><g class="shape" ><path d="M 3594 1306 C 3594 1282 3718 1282 3732 1282 C 3746 1282 3870 1282 3870 1306 V 1440 C 3870 1464 3746 1464 3732 1464 C 3718 1464 3594 1464 3594 1440 V 1306 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /><path d="M 3594 1306 C 3594 1330 3718 1330 3732 1330 C 3746 1330 3870 1330 3870 1306" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="3732.000000" y="1358.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="3732.000000" dy="0.000000">Meilisearch :7700</tspan><tspan x="3732.000000" dy="17.000000">index: books</tspan><tspan x="3732.000000" dy="17.000000">(filterable: status · genres</tspan><tspan x="3732.000000" dy="17.000000"> sortable: rank · rating</tspan><tspan x="3732.000000" dy="17.000000"> total_chapters · meta_updated)</tspan></text></g><g class="YXBwLmNhZGR5"><g class="shape" ><rect x="1081.000000" y="622.000000" width="301.000000" height="178.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="1231.500000" y="660.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="1231.500000" dy="0.000000">Caddy :443 / :80 / :6380</tspan><tspan x="1231.500000" dy="16.625000">custom build</tspan><tspan x="1231.500000" dy="16.625000">+ caddy-l4 (Redis TCP proxy)</tspan><tspan x="1231.500000" dy="16.625000">+ caddy-ratelimit</tspan><tspan x="1231.500000" dy="16.625000">auto-HTTPS · security headers</tspan><tspan x="1231.500000" dy="16.625000">rate limiting (per-IP)</tspan><tspan x="1231.500000" dy="16.625000">static error pages (404/502/503/504)</tspan><tspan x="1231.500000" dy="16.625000">CrowdSec bouncer</tspan></text></g><g class="YXBwLmJhY2tlbmQ="><g class="shape" ><rect x="2671.000000" y="1079.000000" width="246.000000" height="146.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="2794.000000" y="1117.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="2794.000000" dy="0.000000">Backend API :8080</tspan><tspan x="2794.000000" dy="16.833333">(Go)</tspan><tspan x="2794.000000" dy="16.833333">HTTP API server</tspan><tspan x="2794.000000" dy="16.833333">ffmpeg (audio sample conv.)</tspan><tspan x="2794.000000" dy="16.833333">OpenTelemetry tracing</tspan><tspan x="2794.000000" dy="16.833333">Sentry / GlitchTip errors</tspan></text></g><g class="YXBwLnVp"><g class="shape" ><rect x="1943.000000" y="1025.000000" width="196.000000" height="114.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="2041.000000" y="1063.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="2041.000000" dy="0.000000">SvelteKit UI :3000</tspan><tspan x="2041.000000" dy="17.250000">(adapter-node)</tspan><tspan x="2041.000000" dy="17.250000">SSR · session auth</tspan><tspan x="2041.000000" dy="17.250000">server-side API proxy</tspan></text></g><g class="YXBwLmNyb3dkc2Vj"><g class="shape" ><rect x="1917.000000" y="559.000000" width="247.000000" height="114.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="2040.500000" y="597.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="2040.500000" dy="0.000000">CrowdSec :8080</tspan><tspan x="2040.500000" dy="17.250000">security engine</tspan><tspan x="2040.500000" dy="17.250000">reads Caddy JSON logs</tspan><tspan x="2040.500000" dy="17.250000">bouncer integrated in Caddy</tspan></text></g><g class="YXBwLmRvenpsZQ=="><g class="shape" ><rect x="2662.000000" y="784.000000" width="264.000000" height="98.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="2794.000000" y="822.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="2794.000000" dy="0.000000">Dozzle agent</tspan><tspan x="2794.000000" dy="17.666667">127.0.0.1:7007</tspan><tspan x="2794.000000" dy="17.666667">log relay → homelab dashboard</tspan></text></g><g class="aG9tZWxhYi5ydW5uZXI="><g class="shape" ><rect x="2685.000000" y="1804.000000" width="219.000000" height="162.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="2794.500000" y="1842.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="2794.500000" dy="0.000000">Runner :9091</tspan><tspan x="2794.500000" dy="16.714286">(Go background worker)</tspan><tspan x="2794.500000" dy="16.714286">scrape pipeline</tspan><tspan x="2794.500000" dy="16.714286">TTS audio job queue</tspan><tspan x="2794.500000" dy="16.714286">Prometheus /metrics</tspan><tspan x="2794.500000" dy="16.714286">cron: catalogue refresh</tspan><tspan x="2794.500000" dy="16.714286">Asynq worker → Valkey</tspan></text></g><g class="b3BzLndhdGNodG93ZXI="><g class="shape" ><rect x="1096.000000" y="1759.000000" width="271.000000" height="146.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="1231.500000" y="1797.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="1231.500000" dy="0.000000">Watchtower</tspan><tspan x="1231.500000" dy="16.833333">(containrrr/watchtower)</tspan><tspan x="1231.500000" dy="16.833333">polls Docker Hub every 5 min</tspan><tspan x="1231.500000" dy="16.833333">autopulls + redeploys:</tspan><tspan x="1231.500000" dy="16.833333"> backend · ui</tspan><tspan x="1231.500000" dy="16.833333">(runner: label-disabled on prod)</tspan></text></g><g class="Y2ljZC5naXRlYQ=="><g class="shape" ><rect x="122.000000" y="1719.000000" width="271.000000" height="226.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="257.500000" y="1757.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"><tspan x="257.500000" dy="0.000000">Gitea Actions</tspan><tspan x="257.500000" dy="16.454545">(homelab runner)</tspan><tspan x="257.500000" dy="16.454545">tag v* trigger:</tspan><tspan x="257.500000" dy="16.454545"> test-backend</tspan><tspan x="257.500000" dy="16.454545"> check-ui (type-check + build)</tspan><tspan x="257.500000" dy="16.454545"> docker-backend</tspan><tspan x="257.500000" dy="16.454545"> docker-runner</tspan><tspan x="257.500000" dy="16.454545"> docker-ui (bakes releases.json)</tspan><tspan x="257.500000" dy="16.454545"> docker-caddy</tspan><tspan x="257.500000" dy="16.454545"> → push Docker Hub</tspan><tspan x="257.500000" dy="16.454545"> → Gitea Release</tspan></text></g><g class="KGluaXQubWluaW8taW5pdCAtJmd0OyBzdG9yYWdlLm1pbmlvKVswXQ=="><marker id="mk-d2-286243964-2177206569" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" fill="#0D32B2" class="connection fill-B2" stroke-width="2" /> </marker><path d="M 2892.500000 161.750000 C 3016.500000 161.750000 3090.399902 161.750000 3154.000000 161.750000 C 3217.600098 161.750000 3513.800049 558.200012 3678.829364 813.640187" stroke="#0D32B2" fill="none" class="connection stroke-B2" style="stroke-width:2;stroke-dasharray:8.000000,7.892511;" marker-end="url(#mk-d2-286243964-2177206569)" mask="url(#d2-286243964)" /><text x="3360.500000" y="358.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px">create buckets</text></g><g class="KGluaXQucGItaW5pdCAtJmd0OyBzdG9yYWdlLnBvY2tldGJhc2UpWzBd"><path d="M 2900.500000 327.750000 C 3018.100098 327.750000 3090.399902 327.750000 3154.000000 327.750000 C 3217.600098 327.750000 3502.000000 587.000000 3618.000555 588.933343" stroke="#0D32B2" fill="none" class="connection stroke-B2" style="stroke-width:2;stroke-dasharray:8.000000,7.892511;" marker-end="url(#mk-d2-286243964-2177206569)" mask="url(#d2-286243964)" /><text x="3286.500000" y="396.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px">bootstrap schema</text></g><g class="YXBwLihjYWRkeSAtJmd0OyB1aSlbMF0="><marker id="mk-d2-286243964-3488378134" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" fill="#0D32B2" class="connection fill-B1" stroke-width="2" /> </marker><path d="M 1353.605054 800.943232 C 1473.599976 890.150024 1756.900024 936.512024 1938.939722 1029.738712" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="1643.500000" y="915.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="1643.500000" dy="0.000000">/* (catch-all)</tspan><tspan x="1643.500000" dy="18.500000">SvelteKit — auth enforced</tspan></text></g><g class="YXBwLihjYWRkeSAtJmd0OyBiYWNrZW5kKVswXQ=="><path d="M 1383.886266 765.414832 C 1479.599976 799.150024 1545.300049 807.750000 1607.250000 807.750000 C 1669.199951 807.750000 1776.500000 807.750000 1875.500000 807.750000 C 1974.500000 807.750000 2102.800049 807.750000 2196.250000 807.750000 C 2289.699951 807.750000 2580.010986 861.900024 2737.681974 1075.282958" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="2115.000000" y="749.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="2115.000000" dy="0.000000">/health /scrape*</tspan><tspan x="2115.000000" dy="16.555556">/api/browse /api/catalogue</tspan><tspan x="2115.000000" dy="16.555556">/api/ranking /api/version</tspan><tspan x="2115.000000" dy="16.555556">/api/book-preview/*</tspan><tspan x="2115.000000" dy="16.555556">/api/chapter-text/*</tspan><tspan x="2115.000000" dy="16.555556">/api/chapter-markdown/*</tspan><tspan x="2115.000000" dy="16.555556">/api/reindex/* /api/cover/*</tspan><tspan x="2115.000000" dy="16.555556">/api/audio-proxy/* /api/voices</tspan><tspan x="2115.000000" dy="16.555556">/api/audio* /api/presign/*</tspan></text></g><g class="KGFwcC5jYWRkeSAtJmd0OyBzdG9yYWdlLm1pbmlvKVswXQ=="><path d="M 1383.999933 712.266393 C 1479.599976 713.049988 1545.300049 713.250000 1607.250000 713.250000 C 1669.199951 713.250000 1776.500000 713.250000 1875.500000 713.250000 C 1974.500000 713.250000 2102.800049 713.250000 2196.250000 713.250000 C 2289.699951 713.250000 2389.600098 713.250000 2446.000000 713.250000 C 2502.399902 713.250000 2590.800049 713.250000 2667.000000 713.250000 C 2743.199951 713.250000 2844.800049 713.250000 2921.000000 713.250000 C 2997.199951 713.250000 3090.399902 713.250000 3154.000000 713.250000 C 3217.600098 713.250000 3505.399902 884.200012 3635.005171 890.796671" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="2534.000000" y="703.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="2534.000000" dy="0.000000">/avatars/* /audio/*</tspan><tspan x="2534.000000" dy="17.666667">/chapters/*</tspan><tspan x="2534.000000" dy="17.666667">(presigned GETs)</tspan></text></g><g class="YXBwLihjYWRkeSAtJmd0OyBjcm93ZHNlYylbMF0="><path d="M 1383.891075 657.598974 C 1479.599976 624.650024 1751.900024 616.250000 1913.500000 616.250000" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="1647.000000" y="625.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px">bouncer check (15 s poll)</text></g><g class="KGFwcC5jYWRkeSAtJmd0OyBsZXRzZW5jcnlwdClbMF0="><path d="M 1383.759087 627.548363 C 1479.599976 575.700012 1756.599976 528.000000 1937.797501 392.396664" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="1676.500000" y="547.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px">ACME cert (TLS-ALPN-01)</text></g><g class="YXBwLih1aSAtJmd0OyBiYWNrZW5kKVswXQ=="><path d="M 2141.451232 1104.131963 C 2309.500000 1141.937988 2566.199951 1151.500000 2667.000000 1151.500000" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="2403.500000" y="1143.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="2403.500000" dy="0.000000">internal REST proxy</tspan><tspan x="2403.500000" dy="18.500000">(server-side only)</tspan></text></g><g class="KGFwcC51aSAtJmd0OyBzdG9yYWdlLnBvY2tldGJhc2UpWzBd"><path d="M 2141.337484 1039.038285 C 2309.500000 966.765015 2389.600098 948.500000 2446.000000 948.500000 C 2502.399902 948.500000 2590.800049 948.500000 2667.000000 948.500000 C 2743.199951 948.500000 2844.800049 948.500000 2921.000000 948.500000 C 2997.199951 948.500000 3090.399902 948.500000 3154.000000 948.500000 C 3217.600098 948.500000 3513.600098 1033.400024 3678.230737 699.587438" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="3012.000000" y="938.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="3012.000000" dy="0.000000">auth · sessions</tspan><tspan x="3012.000000" dy="17.666667">progress · library</tspan><tspan x="3012.000000" dy="17.666667">comments</tspan></text></g><g class="KGFwcC5iYWNrZW5kIC0mZ3Q7IHN0b3JhZ2UubWluaW8pWzBd"><path d="M 2868.414214 1226.164214 C 3011.800049 1369.550049 3090.399902 1405.750000 3154.000000 1405.750000 C 3217.600098 1405.750000 3515.800049 1320.199951 3689.177100 981.560482" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="3350.000000" y="1365.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="3350.000000" dy="0.000000">chapter objs · audio MP3s</tspan><tspan x="3350.000000" dy="18.500000">avatars · browse cache</tspan></text></g><g class="KGFwcC5iYWNrZW5kIC0mZ3Q7IHN0b3JhZ2UucG9ja2V0YmFzZSlbMF0="><path d="M 2890.582654 1226.725310 C 3016.199951 1324.099976 3090.399902 1348.750000 3154.000000 1348.750000 C 3217.600098 1348.750000 3516.600098 1218.400024 3693.705079 700.784598" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="3451.500000" y="1244.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="3451.500000" dy="0.000000">books · scrape_jobs</tspan><tspan x="3451.500000" dy="18.500000">audio_cache · ranking</tspan></text></g><g class="KGFwcC5iYWNrZW5kIC0mZ3Q7IHN0b3JhZ2UudmFsa2V5KVswXQ=="><path d="M 2918.752715 1220.713323 C 3021.800049 1277.349976 3090.399902 1291.750000 3154.000000 1291.750000 C 3217.600098 1291.750000 3511.600098 1347.800049 3667.691895 1568.733098" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="3343.500000" y="1313.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="3343.500000" dy="0.000000">presign URL cache</tspan><tspan x="3343.500000" dy="18.500000">(SET/GET TTL ~55 min)</tspan></text></g><g class="KGFwcC5iYWNrZW5kIC0mZ3Q7IHN0b3JhZ2UubWVpbGlzZWFyY2gpWzBd"><path d="M 2918.942874 1181.724595 C 3021.800049 1206.849976 3090.399902 1213.250000 3154.000000 1213.250000 C 3217.600098 1213.250000 3498.600098 1229.599976 3601.592269 1292.905396" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="3269.000000" y="1214.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="3269.000000" dy="0.000000">catalogue search</tspan><tspan x="3269.000000" dy="18.500000">facets: genres · status</tspan></text></g><g class="KGFwcC5iYWNrZW5kIC0mZ3Q7IHBvY2tldHR0cylbMF0="><path d="M 2918.823583 1095.678693 C 3021.800049 1049.300049 3090.399902 1037.500000 3154.000000 1037.500000 C 3217.600098 1037.500000 3499.199951 117.599998 3604.000027 117.985294" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="3329.000000" y="672.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="3329.000000" dy="0.000000">voice sample gen.</tspan><tspan x="3329.000000" dy="18.500000">(on-demand · ffmpeg conv.)</tspan></text></g><g class="KGhvbWVsYWIucnVubmVyIC0mZ3Q7IG5vdmVsZmlyZSlbMF0="><path d="M 2861.269047 1967.795807 C 3010.399902 2149.449951 3090.399902 2195.250000 3154.000000 2195.250000 C 3217.600098 2195.250000 3506.800049 2195.199951 3642.000004 2195.005746" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="3203.500000" y="2193.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="3203.500000" dy="0.000000">HTTP scrape</tspan><tspan x="3203.500000" dy="18.500000">HTML → Markdown</tspan></text></g><g class="KGhvbWVsYWIucnVubmVyIC0mZ3Q7IGtva29ybylbMF0="><path d="M 2906.300468 1938.214806 C 3019.300049 1992.868042 3090.399902 2006.750000 3154.000000 2006.750000 C 3217.600098 2006.750000 3500.000000 2006.800049 3608.000006 2006.992859" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="3251.000000" y="2004.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="3251.000000" dy="0.000000">TTS generation</tspan><tspan x="3251.000000" dy="18.500000">text → MP3</tspan></text></g><g class="KGhvbWVsYWIucnVubmVyIC0mZ3Q7IHN0b3JhZ2UubWluaW8pWzBd"><path d="M 2906.445713 1858.420238 C 3019.300049 1831.576050 3090.399902 1824.750000 3154.000000 1824.750000 C 3217.600098 1824.750000 3519.199951 1655.599976 3706.924902 982.852813" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="3524.500000" y="1634.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="3524.500000" dy="0.000000">write chapters</tspan><tspan x="3524.500000" dy="18.500000">&amp; audio MP3s</tspan></text></g><g class="KGhvbWVsYWIucnVubmVyIC0mZ3Q7IHN0b3JhZ2UucG9ja2V0YmFzZSlbMF0="><path d="M 2906.314326 1833.469476 C 3019.300049 1781.062012 3090.399902 1767.750000 3154.000000 1767.750000 C 3217.600098 1767.750000 3519.000000 1553.800049 3706.141755 701.906842" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="3538.000000" y="1463.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="3538.000000" dy="0.000000">read/update scrape_jobs</tspan><tspan x="3538.000000" dy="18.500000">write book records</tspan></text></g><g class="KGhvbWVsYWIucnVubmVyIC0mZ3Q7IHN0b3JhZ2UubWVpbGlzZWFyY2gpWzBd"><path d="M 2904.751073 1802.558114 C 3019.029053 1717.750000 3090.399902 1696.250000 3154.000000 1696.250000 C 3217.600098 1696.250000 3507.600098 1649.000000 3647.592975 1463.194719" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="3309.000000" y="1679.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="3309.000000" dy="0.000000">index books</tspan><tspan x="3309.000000" dy="18.500000">(on scrape completion)</tspan></text></g><g class="KGhvbWVsYWIucnVubmVyIC0mZ3Q7IHN0b3JhZ2UudmFsa2V5KVswXQ=="><path d="M 2879.242024 1802.360051 C 3013.959961 1672.150024 3090.399902 1639.250000 3154.000000 1639.250000 C 3217.600098 1639.250000 3496.600098 1639.800049 3591.000999 1641.910593" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="3206.000000" y="1637.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="3206.000000" dy="0.000000">Asynq job queue</tspan><tspan x="3206.000000" dy="18.500000">(task consume)</tspan></text></g><g class="KGJyb3dzZXIgLSZndDsgYXBwLmNhZGR5KVswXQ=="><path d="M 303.999999 710.997652 C 472.399994 710.799988 559.400024 710.750000 626.000000 710.750000 C 692.599976 710.750000 983.400024 710.750000 1077.000000 710.750000" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="691.500000" y="708.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="691.500000" dy="0.000000">HTTPS :443</tspan><tspan x="691.500000" dy="18.500000">(single entry point)</tspan></text></g><g class="KG9wcy53YXRjaHRvd2VyIC0mZ3Q7IGFwcC5iYWNrZW5kKVswXQ=="><path d="M 1368.991056 1844.438933 C 1476.599976 1854.650024 1545.300049 1776.650024 1607.250000 1655.750000 C 1669.199951 1534.849976 1776.500000 1454.250000 1875.500000 1454.250000 C 1974.500000 1454.250000 2102.800049 1454.250000 2196.250000 1454.250000 C 2289.699951 1454.250000 2578.399902 1408.449951 2729.430057 1228.315190" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="1988.500000" y="1460.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px">watch (label-enabled)</text></g><g class="KG9wcy53YXRjaHRvd2VyIC0mZ3Q7IGFwcC51aSlbMF0="><path d="M 1368.956644 1802.835821 C 1476.599976 1780.050049 1764.099976 1336.050049 1975.553210 1141.954890" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="1668.500000" y="1488.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px">watch (label-enabled)</text></g><g class="KGNpY2QuZ2l0ZWEgLSZndDsgb3BzLndhdGNodG93ZXIpWzBd"><path d="M 395.000000 1832.000000 C 490.600006 1832.000000 559.400024 1832.000000 626.000000 1832.000000 C 692.599976 1832.000000 986.400024 1832.000000 1092.000000 1832.000000" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-286243964-3488378134)" mask="url(#d2-286243964)" /><text x="745.000000" y="1830.000000" fill="#676C7E" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="745.000000" dy="0.000000">push to Docker Hub</tspan><tspan x="745.000000" dy="18.500000">→ Watchtower detects new tag</tspan></text></g><mask id="d2-286243964" maskUnits="userSpaceOnUse" x="-9" y="-101" width="4010" height="2454">
<rect x="-9" y="-101" width="4010" height="2454" fill="white"></rect>
<rect x="3310.000000" y="342.000000" width="101" height="21" fill="black"></rect>
<rect x="3225.000000" y="380.000000" width="123" height="21" fill="black"></rect>
<rect x="1557.000000" y="899.000000" width="173" height="37" fill="black"></rect>
<rect x="2011.000000" y="733.000000" width="208" height="149" fill="black"></rect>
<rect x="2466.000000" y="687.000000" width="136" height="53" fill="black"></rect>
<rect x="1565.000000" y="609.000000" width="164" height="21" fill="black"></rect>
<rect x="1594.000000" y="531.000000" width="165" height="21" fill="black"></rect>
<rect x="2337.000000" y="1127.000000" width="133" height="37" fill="black"></rect>
<rect x="2954.000000" y="922.000000" width="116" height="53" fill="black"></rect>
<rect x="3264.000000" y="1349.000000" width="172" height="37" fill="black"></rect>
<rect x="3377.000000" y="1228.000000" width="149" height="37" fill="black"></rect>
<rect x="3268.000000" y="1297.000000" width="151" height="37" fill="black"></rect>
<rect x="3196.000000" y="1198.000000" width="146" height="37" fill="black"></rect>
<rect x="3237.000000" y="656.000000" width="184" height="37" fill="black"></rect>
<rect x="3140.000000" y="2177.000000" width="127" height="37" fill="black"></rect>
<rect x="3199.000000" y="1988.000000" width="104" height="37" fill="black"></rect>
<rect x="3475.000000" y="1618.000000" width="99" height="37" fill="black"></rect>
<rect x="3453.000000" y="1447.000000" width="170" height="37" fill="black"></rect>
<rect x="3232.000000" y="1663.000000" width="154" height="37" fill="black"></rect>
<rect x="3150.000000" y="1621.000000" width="112" height="37" fill="black"></rect>
<rect x="628.000000" y="692.000000" width="127" height="37" fill="black"></rect>
<rect x="1915.000000" y="1444.000000" width="147" height="21" fill="black"></rect>
<rect x="1595.000000" y="1472.000000" width="147" height="21" fill="black"></rect>
<rect x="643.000000" y="1814.000000" width="204" height="37" fill="black"></rect>
</mask></svg></svg>