PageSourceSearch

https://ko4bep.net/blog/

html ko4bep.net collected 2026-10-07 14:49:19 UTC 107,410 bytes, 1,306 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en-US">
3<head>
4	<meta charset="UTF-8" />
5	<meta name="viewport" content="width=device-width, initial-scale=1" />
6<meta name='robots' content='max-image-preview:large' />
7<title>KO4BEP :: Blog &#8211; nonsense.</title>
8<link rel="alternate" type="application/rss+xml" title="KO4BEP :: Blog &raquo; Feed" href="https://ko4bep.net/blog/index.php/feed/" />
9<link rel="alternate" type="application/rss+xml" title="KO4BEP :: Blog &raquo; Comments Feed" href="https://ko4bep.net/blog/index.php/comments/feed/" />
10<style id="wp-img-auto-sizes-contain-inline-css">
11img:is([sizes=auto i],[sizes^="auto," i]){contain-intrinsic-size:3000px 1500px}
12/*# sourceURL=wp-img-auto-sizes-contain-inline-css */
13</style>
14<style id="wp-block-site-title-inline-css">
15.wp-block-site-title{box-sizing:border-box}.wp-block-site-title :where(a){color:inherit;font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;text-decoration:inherit}
16/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/site-title/style.min.css */
17</style>
18<style id="wp-block-navigation-inline-css">
19.wp-block-navigation{position:relative}.wp-block-navigation ul{margin-bottom:0;margin-left:0;margin-top:0;padding-left:0}.wp-block-navigation ul,.wp-block-navigation ul li{list-style:none}.wp-block-navigation .wp-block-navigation-item{align-items:center;background-color:inherit;display:flex;position:relative}.wp-block-navigation .wp-block-navigation-item .wp-block-navigation__submenu-container:empty{display:none}.wp-block-navigation .wp-block-navigation-item__content{display:block;z-index:1}.wp-block-navigation .wp-block-navigation-item__content.wp-block-navigation-item__content{color:inherit}.wp-block-navigation.has-text-decoration-underline .wp-block-navigation-item__content,.wp-block-navigation.has-text-decoration-underline .wp-block-navigation-item__content:active,.wp-block-navigation.has-text-decoration-underline .wp-block-navigation-item__content:focus{text-decoration:underline}.wp-block-navigation.has-text-decoration-line-through .wp-block-navigation-item__content,.wp-block-navigation.has-text-decoration-line-through .wp-block-navigation-item__content:active,.wp-block-navigation.has-text-decoration-line-through .wp-block-navigation-item__content:focus{text-decoration:line-through}.wp-block-navigation :where(a),.wp-block-navigation :where(a:active),.wp-block-navigation :where(a:focus){text-decoration:none}.wp-block-navigation .wp-block-navigation__submenu-icon{align-self:center;background-color:inherit;border:none;color:currentColor;display:inline-block;font-size:inherit;height:.6em;line-height:0;margin-left:.25em;padding:0;width:.6em}.wp-block-navigation .wp-block-navigation__submenu-icon svg{stroke:currentColor;display:inline-block;height:inherit;margin-top:.075em;width:inherit}.wp-block-navigation{--navigation-layout-justification-setting:flex-start;--navigation-layout-direction:row;--navigation-layout-wrap:wrap;--navigation-layout-justify:flex-start;--navigation-layout-align:center}.wp-block-navigation.is-vertical{--navigation-layout-direction:column;--navigation-layout-justify:initial;--navigation-layout-align:flex-start}.wp-block-navigation.no-wrap{--navigation-layout-wrap:nowrap}.wp-block-navigation.items-justified-center{--navigation-layout-justification-setting:center;--navigation-layout-justify:center}.wp-block-navigation.items-justified-center.is-vertical{--navigation-layout-align:center}.wp-block-navigation.items-justified-right{--navigation-layout-justification-setting:flex-end;--navigation-layout-justify:flex-end}.wp-block-navigation.items-justified-right.is-vertical{--navigation-layout-align:flex-end}.wp-block-navigation.items-justified-space-between{--navigation-layout-justification-setting:space-between;--navigation-layout-justify:space-between}:where(.wp-block-navigation) ul li{padding:0}.wp-block-navigation .has-child .wp-block-navigation__submenu-container{align-items:normal;background-color:inherit;color:inherit;display:flex;flex-direction:column;opacity:0;position:absolute;z-index:2}@media not (prefers-reduced-motion){.wp-block-navigation .has-child .wp-block-navigation__submenu-container{transition:opacity .1s linear}}.wp-block-navigation .has-child .wp-block-navigation__submenu-container{height:0;overflow:hidden;visibility:hidden;width:0}.wp-block-navigation .has-child .wp-block-navigation__submenu-container>.wp-block-navigation-item>.wp-block-navigation-item__content{display:flex;flex-grow:1;padding:.5em 1em}.wp-block-navigation .has-child .wp-block-navigation__submenu-container>.wp-block-navigation-item>.wp-block-navigation-item__content .wp-block-navigation__submenu-icon{margin-left:auto;margin-right:0}.wp-block-navigation .has-child .wp-block-navigation__submenu-container .wp-block-navigation-item__content{margin:0}.wp-block-navigation .has-child .wp-block-navigation__submenu-container{left:-1px;top:100%}@media (min-width:782px){.wp-block-navigation .has-child .wp-block-navigation__submenu-container .wp-block-navigation__submenu-container{left:100%;top:-1px}.wp-block-navigation .has-child .wp-block-navigation__submenu-container .wp-block-navigation__submenu-container:before{background:transparent;content:"";display:block;height:100%;position:absolute;right:100%;width:.5em}.wp-block-navigation .has-child .wp-block-navigation__submenu-container .wp-block-navigation__submenu-icon{margin-right:.25em}.wp-block-navigation .has-child .wp-block-navigation__submenu-container .wp-block-navigation__submenu-icon svg{transform:rotate(-90deg)}}@media (hover:hover){.wp-block-navigation .has-child:not(.open-on-click):hover>.wp-block-navigation__submenu-container{height:auto;
19min-width:200px;opacity:1;overflow:visible;visibility:visible;width:auto}}.wp-block-navigation .has-child .wp-block-navigation-submenu__toggle[aria-expanded=true]~.wp-block-navigation__submenu-container,.wp-block-navigation .has-child:not(.open-on-click):not(.open-on-hover-click):focus-within>.wp-block-navigation__submenu-container{height:auto;min-width:200px;opacity:1;overflow:visible;visibility:visible;width:auto}.wp-block-navigation .has-child.open-always{align-items:var(--navigation-layout-align,initial);flex-direction:var(--navigation-layout-direction,initial);flex-wrap:var(--navigation-layout-wrap,wrap);gap:var(--wp--style--block-gap,2em)}.wp-block-navigation .has-child.open-always,.wp-block-navigation .has-child.open-always .wp-block-navigation-item{justify-content:var(--navigation-layout-justify,initial)}.wp-block-navigation .has-child.open-always .wp-block-navigation__submenu-container,.wp-block-navigation .has-child.open-always.wp-block-navigation-submenu{gap:var(--wp--style--block-gap,2em)}.wp-block-navigation .has-child.open-always .wp-block-navigation-item,.wp-block-navigation .has-child.open-always .wp-block-navigation__submenu-container,.wp-block-navigation .has-child.open-always.wp-block-navigation-submenu{padding-bottom:0;padding-top:0}.wp-block-navigation .has-child.open-always .wp-block-navigation__submenu-container{padding-left:var(--wp--style--block-gap,2em);padding-right:var(--wp--style--block-gap,2em)}.wp-block-navigation .has-child.open-always .wp-block-navigation__submenu-container .wp-block-navigation-item__content{padding:0}.wp-block-navigation .has-child.open-always .wp-block-navigation__submenu-container>.wp-block-navigation-item>.wp-block-navigation-item__content,.wp-block-navigation .has-child.open-always>.wp-block-navigation-item__content{flex-grow:0}.wp-block-navigation .has-child.open-always>.wp-block-navigation__submenu-container{background-color:transparent;border:none;color:inherit;flex-basis:100%;height:auto;opacity:1;overflow:visible;position:static;visibility:visible;width:auto}.wp-block-navigation.has-background .has-child .wp-block-navigation__submenu-container{left:0;top:100%}@media (min-width:782px){.wp-block-navigation.has-background .has-child .wp-block-navigation__submenu-container .wp-block-navigation__submenu-container{left:100%;top:0}}.wp-block-navigation-submenu{display:flex;position:relative}.wp-block-navigation-submenu .wp-block-navigation__submenu-icon svg{stroke:currentColor}button.wp-block-navigation-item__content{background-color:transparent;border:none;color:currentColor;font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;text-align:left;text-transform:inherit}.wp-block-navigation-submenu__toggle{cursor:pointer}.wp-block-navigation-submenu__toggle[aria-expanded=true]+.wp-block-navigation__submenu-icon>svg,.wp-block-navigation-submenu__toggle[aria-expanded=true]>svg{transform:rotate(180deg)}.wp-block-navigation-item.open-on-click .wp-block-navigation-submenu__toggle{padding-left:0;padding-right:.85em}.wp-block-navigation-item.open-on-click .wp-block-navigation-submenu__toggle+.wp-block-navigation__submenu-icon{margin-left:-.6em;pointer-events:none}.wp-block-navigation-item.open-on-click button.wp-block-navigation-item__content:not(.wp-block-navigation-submenu__toggle){padding:0}.wp-block-navigation .wp-block-page-list,.wp-block-navigation__container,.wp-block-navigation__responsive-close,.wp-block-navigation__responsive-container,.wp-block-navigation__responsive-container-content,.wp-block-navigation__responsive-dialog{gap:inherit}:where(.wp-block-navigation.has-background .wp-block-navigation-item a:not(.wp-element-button)),:where(.wp-block-navigation.has-background .wp-block-navigation-submenu a:not(.wp-element-button)){padding:.5em 1em}:where(.wp-block-navigation .wp-block-navigation__submenu-container .wp-block-navigation-item a:not(.wp-element-button)),:where(.wp-block-navigation .wp-block-navigation__submenu-container .wp-block-navigation-submenu a:not(.wp-element-button)),:where(.wp-block-navigation .wp-block-navigation__submenu-container .wp-block-navigation-submenu button.wp-block-navigation-item__content),:where(.wp-block-navigation .wp-block-navigation__submenu-container .wp-block-pages-list__item button.wp-block-navigation-item__content){padding:.5em 1em}.wp-block-navigation.items-justified-right .wp-block-navigation__container .has-child .wp-block-navigation__submenu-container,.wp-block-navigation.items-justified-right .wp-block-page-list>.has-child .wp-block-navigation__submenu-container,.wp-block-navigation.items-justified-space-between .wp-block-page-list>.has-child:last-child .wp-block-navigation__submenu-container,.wp-block-navigation.items-justified-space-between>.wp-block-navigation__container>.has-child:last-child .wp-block-navigation__submenu-container{left:auto;right:0}.wp-block-navigation.items-justified-right .wp-block-navigation__container .has-child .wp-block-navigation__submenu-container .wp-block-navigation__submenu-container,.wp-block-navigation.items-justified-right .wp-block-page-list>.has-child .wp-block-navigation__submenu-container .wp-block-navigation__submenu-container,.wp-block-navigation.items-justified-space-between .wp-block-page-list>.has-child:last-child .wp-block-navigation__submenu-container .wp-block-navigation__submenu-container,.wp-block-navigation.items-justified-space-between>.wp-block-navigation__container>.has-child:last-child .wp-block-navigation__submenu-container .wp-block-navigation__submenu-container{left:-1px;right:-1px}@media (min-width:782px){.wp-block-navigation.items-justified-right .wp-block-navigation__container .has-child .wp-block-navigation__submenu-container .wp-block-navigation__submenu-container,.wp-block-navigation.items-justified-right .wp-block-page-list>.has-child .wp-block-navigation__submenu-container .wp-block-navigation__submenu-container,.wp-block-navigation.items-justified-space-between .wp-block-page-list>.has-child:last-child .wp-block-navigation__submenu-container .wp-block-navigation__submenu-container,.wp-block-navigation.items-justified-space-between>.wp-block-navigation__container>.has-child:last-child .wp-block-navigation__submenu-container .wp-block-navigation__submenu-container{left:auto;right:100%}}.wp-block-navigation:not(.has-background) .wp-block-navigation__submenu-container{background-color:#fff;border:1px solid rgba(0,0,0,.15)}.wp-block-navigation.has-background .wp-block-navigation__submenu-container{background-color:inherit}.wp-block-navigation:not(.has-text-color) .wp-block-navigation__submenu-container{c
19olor:#000}.wp-block-navigation__container{align-items:var(--navigation-layout-align,initial);display:flex;flex-direction:var(--navigation-layout-direction,initial);flex-wrap:var(--navigation-layout-wrap,wrap);justify-content:var(--navigation-layout-justify,initial);list-style:none;margin:0;padding-left:0}.wp-block-navigation__container .is-responsive{display:none}.wp-block-navigation__container:only-child,.wp-block-page-list:only-child{flex-grow:1}@keyframes overlay-menu__fade-in-animation{0%{opacity:0;transform:translateY(.5em)}to{opacity:1;transform:translateY(0)}}.wp-block-navigation__responsive-container{bottom:0;display:none;left:0;position:fixed;right:0;top:0}.wp-block-navigation__responsive-container :where(.wp-block-navigation-item a){color:inherit}.wp-block-navigation__responsive-container .wp-block-navigation__responsive-container-content{align-items:var(--navigation-layout-align,initial);display:flex;flex-direction:var(--navigation-layout-direction,initial);flex-wrap:var(--navigation-layout-wrap,wrap);justify-content:var(--navigation-layout-justify,initial)}.wp-block-navigation__responsive-container:not(.is-menu-open.is-menu-open){background-color:inherit!important;color:inherit!important}.wp-block-navigation__responsive-container.is-menu-open{background-color:inherit;display:flex;flex-direction:column}@media not (prefers-reduced-motion){.wp-block-navigation__responsive-container.is-menu-open{animation:overlay-menu__fade-in-animation .1s ease-out;animation-fill-mode:forwards}}.wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay){padding:clamp(1rem,var(--wp--style--root--padding-top),20rem) clamp(1rem,var(--wp--style--root--padding-right),20rem) clamp(1rem,var(--wp--style--root--padding-bottom),20rem) clamp(1rem,var(--wp--style--root--padding-left),20rem)}.wp-block-navigation__responsive-container.is-menu-open{overflow:auto;z-index:100000}.wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .wp-block-navigation__responsive-container-content{padding-top:calc(2rem 
19+ 24px)}.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-container-content{align-items:var(--navigation-layout-justification-setting,inherit);display:flex;flex-direction:column;flex-wrap:nowrap;overflow:visible}.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-container-content,.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-container-content .wp-block-navigation__container,.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-container-content .wp-block-page-list{justify-content:flex-start}.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-container-content .wp-block-navigation__submenu-icon{display:none}.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-container-content .has-child .wp-block-navigation__submenu-container{border:none;height:auto;
19min-width:200px;opacity:1;overflow:initial;padding-left:2rem;padding-right:2rem;position:static;visibility:visible;width:auto}.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-container-content .wp-block-navigation__container,.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-container-content .wp-block-navigation__submenu-container{gap:inherit}.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-container-content .wp-block-navigation__submenu-container{padding-top:var(--wp--style--block-gap,2em)}.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-container-content .wp-block-navigation-item__content{padding:0}.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-container-content .wp-block-navigation-item,.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-container-content .wp-block-navigation__container,.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-container-content .wp-block-page-list{align-items:var(--navigation-layout-justification-setting,initial);display:flex;flex-direction:column}.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation-item,.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation-item .wp-block-navigation__submenu-container,.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__container,.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-page-list{background:transparent!important;color:inherit!important}.wp-block-navigation__responsive-container.is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__submenu-container.wp-block-navigation__submenu-container.wp-block-navigation__submenu-container.wp-block-navigation__submenu-container{left:auto;right:auto}.wp-block-navigation__responsive-container.disable-default-overlay .wp-block-navigation__overlay-container{display:none;width:100%}.wp-block-navigation__responsive-container.disable-default-overlay .wp-block-navigation__responsive-close{max-width:none}.wp-block-navigation__responsive-container.disable-default-overlay.is-menu-open .wp-block-navigation__responsive-container-content>:not(.wp-block-navigation__overlay-container){display:none}.wp-block-navigation__responsive-container.disable-default-overlay.is-menu-open .wp-block-navigation__responsive-container-content .wp-block-navigation__overlay-container{display:block}.wp-block-navigation__responsive-container.disable-default-overlay.is-menu-open .wp-block-navigation__responsive-container-content .wp-block-navigation__overlay-container .wp-block-navigation__submenu-container{left:0;right:auto}@media (min-width:600px){.wp-block-navigation__responsive-container:not(.hidden-by-default):not(.is-menu-open){background-color:inherit;display:block;position:relative;width:100%;z-index:auto}.wp-block-navigation__responsive-container:not(.hidden-by-default):not(.is-menu-open) .wp-block-navigation__responsive-container-close{display:none}.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__submenu-container.wp-block-navigation__submenu-container.wp-block-navigation__submenu-container.wp-block-navigation__submenu-container{left:0}}.wp-block-navigation:not(.has-background) .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay){background-color:#fff}.wp-block-navigation:not(.has-text-color) .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay){color:#000}.wp-block-navigation__toggle_button_label{font-size:1rem;font-weight:700}.wp-block-navigation__responsive-container-close,.wp-block-navigation__responsive-container-open{background:transparent;border:none;color:currentColor;cursor:pointer;margin:0;padding:0;text-transform:inherit;vertical-align:middle}.wp-block-navigation__responsive-container-close svg,.wp-block-navigation__responsive-container-open svg{fill:currentColor;display:block;height:24px;pointer-events:none;width:24px}.wp-block-navigation__responsive-container-open{display:flex}.wp-block-navigation__responsive-container-open.wp-block-navigation__responsive-container-open.wp-block-navigation__responsive-container-open{font-family:inherit;font-size:inherit;font-weight:inherit}@media (min-width:600px){.wp-block-navigation__responsive-container-open:not(.always-shown){display:none}}.wp-block-navigation__responsive-container-close{position:absolute;right:0;top:0;z-index:2}.wp-block-navigation__responsive-container-close.wp-block-navigation__responsive-container-close.wp-block-navigation__responsive-container-close{font-family:inherit;font-size:inherit;font-weight:inherit}.disable-default-overlay .wp-block-navigation__responsive-container-close{right:clamp(1rem,var(--wp--style--root--padding-left),20rem);top:clamp(1rem,var(--wp--style--root--padding-left),20rem)}.wp-block-navigation__responsive-close{width:100%}.has-modal-open .wp-block-navigation__responsive-close{margin-left:auto;margin-right:auto;max-width:var(--wp--style--global--wide-size,100%)}.wp-block-navigation__responsive-close:focus{outline:none}.is-menu-open .wp-block-navigation__responsive-close,.is-menu-open .wp-block-navigation__responsive-container-content,.is-menu-open .wp-block-navigation__responsive-dialog{box-sizing:border-box}.wp-block-navigation__responsive-dialog{position:relative}
19.has-modal-open .admin-bar .is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-dialog{margin-top:46px}@media (min-width:782px){.has-modal-open .admin-bar .is-menu-open:where(:not(.disable-default-overlay)) .wp-block-navigation__responsive-dialog{margin-top:32px}}html.has-modal-open{overflow:hidden}
20/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/navigation/style.min.css */
21</style>
22<style id="wp-block-group-inline-css">
23.wp-block-group{box-sizing:border-box}:where(.wp-block-group.wp-block-group-is-layout-constrained){position:relative}
24/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/group/style.min.css */
25</style>
26<style id="wp-block-heading-inline-css">
27h1:where(.wp-block-heading).has-background,h2:where(.wp-block-heading).has-background,h3:where(.wp-block-heading).has-background,h4:where(.wp-block-heading).has-background,h5:where(.wp-block-heading).has-background,h6:where(.wp-block-heading).has-background{padding:1.25em 2.375em}h1.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h1.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h2.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h2.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h3.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h3.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h4.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h4.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h5.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h5.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h6.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h6.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]){rotate:180deg}
28/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/heading/style.min.css */
29</style>
30<style id="wp-block-post-title-inline-css">
31.wp-block-post-title{box-sizing:border-box;word-break:break-word}.wp-block-post-title :where(a){display:inline-block;font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;text-decoration:inherit}
32/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/post-title/style.min.css */
33</style>
34<style id="wp-block-paragraph-inline-css">
35.is-small-text{font-size:.875em}.is-regular-text{font-size:1em}.is-large-text{font-size:2.25em}.is-larger-text{font-size:3em}.has-drop-cap:not(:focus):first-letter{float:left;font-size:8.4em;font-style:normal;font-weight:100;line-height:.68;margin:.05em .1em 0 0;text-transform:uppercase}body.rtl .has-drop-cap:not(:focus):first-letter{float:none;margin-left:.1em}p.has-drop-cap.has-background{overflow:hidden}:root :where(p.has-background){padding:1.25em 2.375em}:where(p.has-text-color:not(.has-link-color)) a{color:inherit}p.has-text-align-left[style*="writing-mode:vertical-lr"],p.has-text-align-right[style*="writing-mode:vertical-rl"]{rotate:180deg}
36/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/paragraph/style.min.css */
37</style>
38<style id="wp-block-list-inline-css">
39ol,ul{box-sizing:border-box}:root :where(.wp-block-list.has-background){padding:1.25em 2.375em}
40
41				ul.is-style-checkmark-list {
42					list-style-type: "\2713";
43				}
44
45				ul.is-style-checkmark-list li {
46					padding-inline-start: 1ch;
47				}
48/*# sourceURL=wp-block-list-inline-css */
49</style>
50<style id="wp-block-code-inline-css">
51.wp-block-code{box-sizing:border-box}.wp-block-code code{direction:ltr;display:block;font-family:inherit;overflow-wrap:break-word;text-align:initial;white-space:break-spaces}
52/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/code/style.min.css */
53</style>
54<style id="wp-block-post-content-inline-css">
55.wp-block-post-content{display:flow-root}
56/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/post-content/style.min.css */
57</style>
58<style id="wp-block-post-date-inline-css">
59.wp-block-post-date{box-sizing:border-box}
60/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/post-date/style.min.css */
61</style>
62<style id="wp-block-separator-inline-css">
63@charset "UTF-8";.wp-block-separator{border:none;border-top:2px solid}:root :where(.wp-block-separator.is-style-dots){height:auto;line-height:1;text-align:center}:root :where(.wp-block-separator.is-style-dots):before{color:currentColor;content:"···";font-family:serif;font-size:1.5em;letter-spacing:2em;padding-left:2em}.wp-block-separator.is-style-dots{background:none!important;border:none!important}
64/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/separator/style.min.css */
65</style>
66<style id="wp-block-quote-inline-css">
67.wp-block-quote{box-sizing:border-box;overflow-wrap:break-word}.wp-block-quote.is-large:where(:not(.is-style-plain)),.wp-block-quote.is-style-large:where(:not(.is-style-plain)){margin-bottom:1em;padding:0 1em}.wp-block-quote.is-large:where(:not(.is-style-plain)) p,.wp-block-quote.is-style-large:where(:not(.is-style-plain)) p{font-size:1.5em;font-style:italic;line-height:1.6}.wp-block-quote.is-large:where(:not(.is-style-plain)) cite,.wp-block-quote.is-large:where(:not(.is-style-plain)) footer,.wp-block-quote.is-style-large:where(:not(.is-style-plain)) cite,.wp-block-quote.is-style-large:where(:not(.is-style-plain)) footer{font-size:1.125em;text-align:right}.wp-block-quote>cite{display:block}
68/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/quote/style.min.css */
69</style>
70<style id="wp-block-post-template-inline-css">
71.wp-block-post-template{box-sizing:border-box;list-style:none;margin-bottom:0;margin-top:0;max-width:100%;padding:0}.wp-block-post-template.is-flex-container{display:flex;flex-direction:row;flex-wrap:wrap;gap:1.25em}.wp-block-post-template.is-flex-container>li{margin:0;width:100%}@media (min-width:600px){.wp-block-post-template.is-flex-container.is-flex-container.columns-2>li{width:calc(50% - .625em)}.wp-block-post-template.is-flex-container.is-flex-container.columns-3>li{width:calc(33.33333% - .83333em)}.wp-block-post-template.is-flex-container.is-flex-container.columns-4>li{width:calc(25% - .9375em)}.wp-block-post-template.is-flex-container.is-flex-container.columns-5>li{width:calc(20% - 1em)}.wp-block-post-template.is-flex-container.is-flex-container.columns-6>li{width:calc(16.66667% - 1.04167em)}}@media (max-width:600px){.wp-block-post-template-is-layout-grid[class*=columns-]:not(.has-native-responsive-grid){grid-template-columns:1fr}}.wp-block-post-template-is-layout-constrained>li>.alignright,.wp-block-post-template-is-layout-flow>li>.alignright{float:right;margin-inline-end:0;margin-inline-start:2em}.wp-block-post-template-is-layout-constrained>li>.alignleft,.wp-block-post-template-is-layout-flow>li>.alignleft{float:left;margin-inline-end:2em;margin-inline-start:0}.wp-block-post-template-is-layout-constrained>li>.aligncenter,.wp-block-post-template-is-layout-flow>li>.aligncenter{margin-inline-end:auto;margin-inline-start:auto}
72/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/post-template/style.min.css */
73</style>
74<style id="wp-block-site-tagline-inline-css">
75.wp-block-site-tagline{box-sizing:border-box}
76/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/site-tagline/style.min.css */
77</style>
78<style id="wp-block-spacer-inline-css">
79.wp-block-spacer{clear:both}
80/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/spacer/style.min.css */
81</style>
82<style id="wp-block-columns-inline-css">
83.wp-block-columns{box-sizing:border-box;display:flex;flex-wrap:wrap!important}@media (min-width:782px){.wp-block-columns{flex-wrap:nowrap!important}}.wp-block-columns{align-items:normal!important}.wp-block-columns.are-vertically-aligned-top{align-items:flex-start}.wp-block-columns.are-vertically-aligned-center{align-items:center}.wp-block-columns.are-vertically-aligned-bottom{align-items:flex-end}@media (max-width:781px){.wp-block-columns:not(.is-not-stacked-on-mobile)>.wp-block-column{flex-basis:100%!important}}@media (min-width:782px){.wp-block-columns:not(.is-not-stacked-on-mobile)>.wp-block-column{flex-basis:0;flex-grow:1}.wp-block-columns:not(.is-not-stacked-on-mobile)>.wp-block-column[style*=flex-basis]{flex-grow:0}}.wp-block-columns.is-not-stacked-on-mobile{flex-wrap:nowrap!important}.wp-block-columns.is-not-stacked-on-mobile>.wp-block-column{flex-basis:0;flex-grow:1}.wp-block-columns.is-not-stacked-on-mobile>.wp-block-column[style*=flex-basis]{flex-grow:0}:where(.wp-block-columns){margin-bottom:1.75em}:where(.wp-block-columns.has-background){padding:1.25em 2.375em}.wp-block-column{flex-grow:1;min-width:0;overflow-wrap:break-word;word-break:break-word}.wp-block-column.is-vertically-aligned-top{align-self:flex-start}.wp-block-column.is-vertically-aligned-center{align-self:center}.wp-block-column.is-vertically-aligned-bottom{align-self:flex-end}.wp-block-column.is-vertically-aligned-stretch{align-self:stretch}.wp-block-column.is-vertically-aligned-bottom,.wp-block-column.is-vertically-aligned-center,.wp-block-column.is-vertically-aligned-top{width:100%}
84/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/columns/style.min.css */
85</style>
86<style id="wp-block-navigation-link-inline-css">
87.wp-block-navigation .wp-block-navigation-item__label{overflow-wrap:break-word}
87.wp-block-navigation .wp-block-navigation-item__description{display:none}.link-ui-tools{outline:1px solid #f0f0f0;padding:8px}.link-ui-block-inserter{padding-top:8px}.link-ui-block-inserter__back{margin-left:8px;text-transform:uppercase}
88/*# sourceURL=https://ko4bep.net/blog/wp-includes/blocks/navigation-link/style.min.css */
89</style>
90<style id="wp-emoji-styles-inline-css">
91
92	img.wp-smiley, img.emoji {
93		display: inline !important;
94		border: none !important;
95		box-shadow: none !important;
96		height: 1em !important;
97		width: 1em !important;
98		margin: 0 0.07em !important;
99		vertical-align: -0.1em !important;
100		background: none !important;
101		padding: 0 !important;
102	}
103/*# sourceURL=wp-emoji-styles-inline-css */
104</style>
105<style id="wp-block-library-inline-css">
106:root{--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122,0,223;--wp-bound-block-color:var(--wp-block-synced-color);--wp-editor-canvas-background:#ddd;--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0,124,186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0,107,160.5;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0,90,135;--wp-admin-border-width-focus:2px}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px}}.wp-element-button{cursor:pointer}:root .has-very-light-gray-background-color{background-color:#eee}:root .has-very-dark-gray-background-color{background-color:#313131}:root .has-very-light-gray-color{color:#eee}:root .has-very-dark-gray-color{color:#313131}:root .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background{background:linear-gradient(135deg,#00d084,#0693e3)}:root .has-purple-crush-gradient-background{background:linear-gradient(135deg,#34e2e4,#4721fb 50%,#ab1dfe)}:root .has-hazy-dawn-gradient-background{background:linear-gradient(135deg,#faaca8,#dad0ec)}:root .has-subdued-olive-gradient-background{background:linear-gradient(135deg,#fafae1,#67a671)}:root .has-atomic-cream-gradient-background{background:linear-gradient(135deg,#fdd79a,#004a59)}:root .has-nightshade-gradient-background{background:linear-gradient(135deg,#330968,#31cdcf)}:root .has-midnight-gradient-background{background:linear-gradient(135deg,#020381,#2874fc)}:root{--wp--preset--font-size--normal:16px;
106--wp--preset--font-size--huge:42px}.has-regular-font-size{font-size:1em}.has-larger-font-size{font-size:2.625em}.has-normal-font-size{font-size:var(--wp--preset--font-size--normal)}.has-huge-font-size{font-size:var(--wp--preset--font-size--huge)}:root .has-text-align-center{text-align:center}:root .has-text-align-left{text-align:left}:root .has-text-align-right{text-align:right}.has-fit-text{white-space:nowrap!important}#end-resizable-editor-section{display:none}.aligncenter{clear:both}.items-justified-left{justify-content:flex-start}.items-justified-center{justify-content:center}.items-justified-right{justify-content:flex-end}.items-justified-space-between{justify-content:space-between}.screen-reader-text{word-wrap:normal!important;border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;word-break:normal!important}.screen-reader-text:focus{background-color:#ddd;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}html :where(.has-border-color){border-style:solid}html :where([style^=border-color],[style*=";border-color"],[style*="; border-color"]){border-style:solid}html :where([style^=border-top-color],[style*=";border-top-color"],[style*="; border-top-color"]){border-top-style:solid}html :where([style^=border-right-color],[style*=";border-right-color"],[style*="; border-right-color"]){border-right-style:solid}html :where([style^=border-bottom-color],[style*=";border-bottom-color"],[style*="; border-bottom-color"]){border-bottom-style:solid}html :where([style^=border-left-color],[style*=";border-left-color"],[style*="; border-left-color"]){border-left-style:solid}html :where([style^=border-width],[style*=";border-width"],[style*="; border-width"]){border-style:solid}html :where([style^=border-top-width],[style*=";border-top-width"],[style*="; border-top-width"]){border-top-style:solid}html :where([style^=border-right-width],[style*=";border-right-width"],[style*="; border-right-width"]){border-right-style:solid}html :where([style^=border-bottom-width],[style*=";border-bottom-width"],[style*="; border-bottom-width"]){border-bottom-style:solid}html :where([style^=border-left-width],[style*=";border-left-width"],[style*="; border-left-width"]){border-left-style:solid}html :where(img[class*=wp-image-]){height:auto;max-width:100%}:where(figure){margin:0 0 1em}html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:var(--wp-admin--admin-bar--height,0px)}@media screen and (max-width:600px){html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:0px}}
107/*# sourceURL=/wp-includes/css/dist/block-library/common.min.css */
108</style>
109<style id="global-styles-inline-css">
110:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--color--base: #131313;--wp--preset--color--contrast: #FFFFFF;--wp--preset--color--accent-1: #4B52FF;--wp--preset--color--accent-2: #FF7A5C;--wp--preset--color--accent-3: #252525;--wp--preset--color--accent-4: #FFFFFF;--wp--preset--color--accent-5: #252525;--wp--preset--color--accent-6: #FFFFFF33;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgb(252,185,
1100) 0%,rgb(255,105,0) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgb(255,105,0) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--font-size--small: 0.875rem;--wp--preset--font-size--medium: clamp(1rem, 1rem + ((1vw - 0.2rem) * 0.196), 1.125rem);--wp--preset--font-size--large: clamp(1.125rem, 1.125rem + ((1vw - 0.2rem) * 0.392), 1.375rem);--wp--preset--font-size--x-large: clamp(1.75rem, 1.75rem + ((1vw - 0.2rem) * 0.392), 2rem);--wp--preset--font-size--xx-large: clamp(1.4rem, 1.4rem + ((1vw - 0.2rem) * 1.882), 2.6rem);--wp--preset--font-family--ysabeau-office: "Ysabeau Office", sans-serif;--wp--preset--font-family--manrope: Manrope, sans-serif;--wp--preset--spacing--20: 10px;--wp--preset--spacing--30: 20px;--wp--preset--spacing--40: 30px;--wp--preset--spacing--50: clamp(30px, 5vw, 50px);--wp--preset--spacing--60: clamp(30px, 7vw, 70px);--wp--preset--spacing--70: clamp(50px, 7vw, 90px);--wp--preset--spacing--80: clamp(70px, 10vw, 140px);--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgb(255, 255, 255), 6px 6px rgb(0, 0, 0);--wp--preset--shadow--crisp: 6px 6px 0px rgb(0, 0, 0);}.wp-block-button{--wp--preset--dimension--25: 25%;--wp--preset--dimension--50: 50%;--wp--preset--dimension--75: 75%;--wp--preset--dimension--100: 100%;}:root { --wp--style--global--content-size: 645px;--wp--style--global--wide-size: 1340px; }:where(body) { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) { padding-right: 0; padding-left: 0; }.has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) > .alignfull { margin-left: 0; margin-right: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 1.2rem; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: 1.2rem; }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: 1.2rem;margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: 1.2rem;margin-block-end: 0;}:root :where(.is-layout-flex){gap: 1.2rem;}:root :where(.is-layout-grid){gap: 1.2rem;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained >
110 :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{background-color: var(--wp--preset--color--base);color: var(--wp--preset--color--contrast);font-family: var(--wp--preset--font-family--ysabeau-office);font-size: var(--wp--preset--font-size--large);font-weight: 300;letter-spacing: -0.24px;line-height: 1.4;--wp--style--root--padding-top: 0px;--wp--style--root--padding-right: var(--wp--preset--spacing--50);--wp--style--root--padding-bottom: 0px;--wp--style--root--padding-left: var(--wp--preset--spacing--50);}a:where(:not(.wp-element-button)){color: currentColor;}:root :where(a:where(:not(.wp-element-button)):hover){text-decoration: none;}h1, h2, h3, h4, h5, h6{font-family: var(--wp--preset--font-family--literata);font-weight: 900;letter-spacing: -0.1px;line-height: 1.2;}h1{font-size: var(--wp--preset--font-size--xx-large);}h2{font-size: var(--wp--preset--font-size--x-large);}h3{font-size: var(--wp--preset--font-size--large);}h4{font-size: var(--wp--preset--font-size--medium);}h5{font-size: var(--wp--preset--font-size--small);letter-spacing: 0px;}h6{font-size: var(--wp--preset--font-size--small);font-weight: 900;letter-spacing: 0px;text-transform: uppercase;}:root :where(.wp-element-button, .wp-block-button__link){background-color: var(--wp--preset--color--contrast);border-radius: 0px;border-width: 0;color: var(--wp--preset--color--base);font-family: var(--wp--preset--font-family--literata);font-size: var(--wp--preset--font-size--medium);font-style: inherit;font-weight: 900;letter-spacing: -0.36px;line-height: inherit;padding-top: 1rem;padding-right: 2.25rem;padding-bottom: 1rem;padding-left: 2.25rem;text-decoration: none;text-transform: inherit;}:root :where(.wp-element-button:hover, .wp-block-button__link:hover){background-color: color-mix(in srgb, var(--wp--preset--color--contrast) 85%, transparent);border-color: transparent;color: var(--wp--preset--color--base);}:root :where(.wp-element-button:focus, .wp-block-button__link:focus){outline-color: var(--wp--preset--color--accent-4);outline-offset: 2px;}:root :where(.wp-element-caption, .wp-block-audio figcaption, .wp-block-embed figcaption, .wp-block-gallery figcaption, .wp-block-image figcaption, .wp-block-table figcaption, .wp-block-video figcaption){font-size: var(--wp--preset--font-size--small);line-height: 1.4;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-base-color{color: var(--wp--preset--color--base) !important;}.has-contrast-color{color: var(--wp--preset--color--contrast) !important;}.has-accent-1-color{color: var(--wp--preset--color--accent-1) !important;}.has-accent-2-color{color: var(--wp--preset--color--accent-2) !important;}.has-accent-3-color{color: var(--wp--preset--color--accent-3) !important;}.has-accent-4-color{color: var(--wp--preset--color--accent-4) !important;}.has-accent-5-color{color: var(--wp--preset--color--accent-5) !important;}.has-accent-6-color{color: var(--wp--preset--color--accent-6) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-base-background-color{background-color: var(--wp--preset--color--base) !important;}.has-contrast-background-color{background-color: var(--wp--preset--color--contrast) !important;}.has-accent-1-background-color{background-color: var(--wp--preset--color--accent-1) !important;}.has-accent-2-background-color{background-color: var(--wp--preset--color--accent-2) !important;}.has-accent-3-background-color{background-color: var(--wp--preset--color--accent-3) !important;}.has-accent-4-background-color{background-color: var(--wp--preset--color--accent-4) !important;}.has-accent-5-background-color{background-color: var(--wp--preset--color--accent-5) !important;}.has-accent-6-background-color{background-color: var(--wp--preset--color--accent-6) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-base-border-color{border-color: var(--wp--preset--color--base) !important;}.has-contrast-border-color{border-color: var(--wp--preset--color--contrast) !important;}.has-accent-1-border-color{border-color: var(--wp--preset--color--accent-1) !important;}.has-accent-2-border-color{border-color: var(--wp--preset--color--accent-2) !important;}.has-accent-3-border-color{border-color: var(--wp--preset--color--accent-3) !important;}.has-accent-4-border-color{border-color: var(--wp--preset--color--accent-4) !important;}.has-accent-5-border-color{border-color: var(--wp--preset--color--accent-5) !important;}.has-accent-6-border-color{border-color: var(--wp--preset--color--accent-6) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}
110.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}.has-xx-large-font-size{font-size: var(--wp--preset--font-size--xx-large) !important;}.has-ysabeau-office-font-family{font-family: var(--wp--preset--font-family--ysabeau-office) !important;}.has-manrope-font-family{font-family: var(--wp--preset--font-family--manrope) !important;}
111:root :where(.wp-block-columns-is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.wp-block-columns-is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.wp-block-columns-is-layout-flow) > *{margin-block-start: var(--wp--preset--spacing--50);margin-block-end: 0;}:root :where(.wp-block-columns-is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.wp-block-columns-is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.wp-block-columns-is-layout-constrained) > *{margin-block-start: var(--wp--preset--spacing--50);margin-block-end: 0;}:root :where(.wp-block-columns-is-layout-flex){gap: var(--wp--preset--spacing--50);}:root :where(.wp-block-columns-is-layout-grid){gap: var(--wp--preset--spacing--50);}
112:root :where(.wp-block-code){background-color: var(--wp--preset--color--accent-5);color: var(--wp--preset--color--contrast);font-family: var(--wp--preset--font-family--fira-code);font-size: var(--wp--preset--font-size--medium);font-weight: 300;padding-top: var(--wp--preset--spacing--40);padding-right: var(--wp--preset--spacing--40);padding-bottom: var(--wp--preset--spacing--40);padding-left: var(--wp--preset--spacing--40);}
113:root :where(.wp-block-post-date){color: var(--wp--preset--color--accent-4);font-size: var(--wp--preset--font-size--small);}
114:root :where(.wp-block-post-date a:where(:not(.wp-element-button))){color: var(--wp--preset--color--accent-4);text-decoration: none;}
115:root :where(.wp-block-post-date a:where(:not(.wp-element-button)):hover){text-decoration: underline;}
116:root :where(.wp-block-post-title){font-weight: 900;letter-spacing: -0.96px;}
117:root :where(.wp-block-post-title a:where(:not(.wp-element-button))){text-decoration: none;}
118:root :where(.wp-block-post-title a:where(:not(.wp-element-button)):hover){text-decoration: underline;}
119:root :where(.wp-block-quote){border-color: currentColor;border-width: 0 0 0 2px;border-style: solid;font-size: var(--wp--preset--font-size--large);font-weight: 300;margin-right: 0;margin-left: 0;padding-top: var(--wp--preset--spacing--30);padding-right: var(--wp--preset--spacing--40);padding-bottom: var(--wp--preset--spacing--30);padding-left: var(--wp--preset--spacing--40);}:root :where(.wp-block-quote-is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.wp-block-quote-is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.wp-block-quote-is-layout-flow) > *{margin-block-start: var(--wp--preset--spacing--30);margin-block-end: 0;}:root :where(.wp-block-quote-is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.wp-block-quote-is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.wp-block-quote-is-layout-constrained) > *{margin-block-start: var(--wp--preset--spacing--30);margin-block-end: 0;}:root :where(.wp-block-quote-is-layout-flex){gap: var(--wp--preset--spacing--30);}:root :where(.wp-block-quote-is-layout-grid){gap: var(--wp--preset--spacing--30);}:root :where(.wp-block-quote.has-text-align-right ){border-width: 0 2px 0 0;}:root :where(.wp-block-quote.has-text-align-center ){border-width: 0;border-inline: 0; padding-inline: 0;}
120:root :where(.wp-block-quote cite){font-size: var(--wp--preset--font-size--medium);font-style: normal;font-weight: 300;letter-spacing: -0.14px;}:root :where(.wp-block-quote cite sub){font-size: 0.65em}
121:root :where(.wp-block-separator){border-color: currentColor;border-width: 0 0 1px 0;border-style: solid;color: var(--wp--preset--color--accent-6);}
122:root :where(.wp-block-site-tagline){font-size: var(--wp--preset--font-size--medium);}
123:root :where(.wp-block-site-title){font-family: var(--wp--preset--font-family--ysabeau-office);font-weight: 700;letter-spacing: 1.6px;text-transform: uppercase;}
124:root :where(.wp-block-site-title a:where(:not(.wp-element-button))){text-decoration: none;}
125:root :where(.wp-block-site-title a:where(:not(.wp-element-button)):hover){text-decoration: underline;}
126:root :where(.wp-block-navigation){font-size: clamp(0.875rem, 0.875rem + ((1vw - 0.2rem) * 0.588), 1.25rem);}
127:root :where(.wp-block-navigation a:where(:not(.wp-element-button))){text-decoration: none;}
128:root :where(.wp-block-navigation a:where(:not(.wp-element-button)):hover){text-decoration: underline;}
129:root :where(.wp-block-list li){margin-top: 0.5rem;}
130/*# sourceURL=global-styles-inline-css */
131</style>
132<style id="core-block-supports-inline-css">
133.wp-container-core-navigation-is-layout-6e943505{justify-content:flex-end;}.wp-container-core-group-is-layout-fbffc046{flex-wrap:nowrap;gap:var(--wp--preset--spacing--10);justify-content:flex-end;}.wp-container-core-group-is-layout-ec1e7a1e{flex-wrap:nowrap;justify-content:space-between;}.wp-container-core-columns-is-layout-794e3cfa{flex-wrap:nowrap;}.wp-container-core-navigation-is-layout-831b2db5{flex-direction:column;align-items:flex-start;}.wp-container-core-group-is-layout-bf19e0b3{gap:var(--wp--preset--spacing--80);justify-content:space-between;align-items:flex-start;}.wp-container-core-group-is-layout-cf54d0a6{justify-content:space-between;align-items:flex-start;}.wp-container-core-group-is-layout-2ab8c7fb{gap:var(--wp--preset--spacing--20);justify-content:space-between;}
134/*# sourceURL=core-block-supports-inline-css */
135</style>
136<style id="wp-block-template-skip-link-inline-css">
137/*! This file is auto-generated */
138.skip-link.screen-reader-text{border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute!important;width:1px;word-wrap:normal!important;word-break:normal!important}.skip-link.screen-reader-text:focus{background-color:#eee;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}
139/*# sourceURL=/wp-includes/css/wp-block-template-skip-link.min.css */
140</style>
141<style id="twentytwentyfive-style-inline-css">
142a{text-decoration-thickness:1px!important;text-underline-offset:.1em}:where(.wp-site-blocks :focus){outline-style:solid;outline-width:2px}.wp-block-navigation .wp-block-navigation-submenu .wp-block-navigation-item:not(:last-child){margin-bottom:3px}.wp-block-navigation .wp-block-navigation-item .wp-block-navigation-item__content{outline-offset:4px}.wp-block-navigation .wp-block-navigation-item ul.wp-block-navigation__submenu-container .wp-block-navigation-item__content{outline-offset:0}blockquote,caption,figcaption,h1,h2,h3,h4,h5,h6,p{text-wrap:pretty}.more-link{display:block}:where(pre){overflow-x:auto}
143/*# sourceURL=https://ko4bep.net/blog/wp-content/themes/twentytwentyfive/style.min.css */
144</style>
145<link rel="https://api.w.org/" href="https://ko4bep.net/blog/index.php/wp-json/" /><link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://ko4bep.net/blog/xmlrpc.php?rsd" />
146<meta name="generator" content="WordPress 7.1" />
147<script id="wp-importmap" type="importmap">
148{"imports":{"@wordpress/interactivity":"https://ko4bep.net/blog/wp-includes/js/dist/script-modules/interactivity/index.min.js?ver=efaa5193bbad9c60ffd1"}}
149</script>
149
150<link rel="modulepreload" href="https://ko4bep.net/blog/wp-includes/js/dist/script-modules/interactivity/index.min.js?ver=efaa5193bbad9c60ffd1" id="@wordpress/interactivity-js-modulepreload" fetchpriority="low">
151<style class="wp-fonts-local">
152@font-face{font-family:"Ysabeau Office";font-style:normal;font-weight:100 900;font-display:fallback;src:url('https://ko4bep.net/blog/wp-content/themes/twentytwentyfive/assets/fonts/ysabeau-office/YsabeauOffice-VariableFont_wght.woff2') format('woff2');}
153@font-face{font-family:"Ysabeau Office";font-style:italic;font-weight:100 900;font-display:fallback;src:url('https://ko4bep.net/blog/wp-content/themes/twentytwentyfive/assets/fonts/ysabeau-office/YsabeauOffice-Italic-VariableFont_wght.woff2') format('woff2');}
154@font-face{font-family:Manrope;font-style:normal;font-weight:200 800;font-display:fallback;src:url('https://ko4bep.net/blog/wp-content/themes/twentytwentyfive/assets/fonts/manrope/Manrope-VariableFont_wght.woff2') format('woff2');}
155</style>
156</head>
157
158<body class="home blog wp-embed-responsive wp-theme-twentytwentyfive">
159
160<a class="skip-link screen-reader-text" id="wp-skip-link" href="#wp--skip-link--target">Skip to content</a><div class="wp-site-blocks"><header class="wp-block-template-part">
161<div class="wp-block-group alignfull is-layout-flow wp-block-group-is-layout-flow">
162	
163	<div class="wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained">
164		
165		<div class="wp-block-group alignwide is-content-justification-space-between is-nowrap is-layout-flex wp-container-core-group-is-layout-ec1e7a1e wp-block-group-is-layout-flex" style="padding-top:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30)">
166			<p class="wp-block-site-title"><a href="https://ko4bep.net/blog" target="_self" rel="home" aria-current="page">KO4BEP :: Blog</a></p>
167			
168			<div class="wp-block-group is-content-justification-right is-nowrap is-layout-flex wp-container-core-group-is-layout-fbffc046 wp-block-group-is-layout-flex">
169				<nav class="is-responsive items-justified-right wp-block-navigation is-content-justification-right is-layout-flex wp-container-core-navigation-is-layout-6e943505 wp-block-navigation-is-layout-flex" 
170		 data-wp-interactive="core/navigation" data-wp-context='{"overlayOpenedBy":{"click":false,"hover":false,"focus":false},"type":"overlay","roleAttribute":"","ariaLabel":"Menu"}'><button aria-haspopup="dialog" aria-label="Open menu" class="wp-block-navigation__responsive-container-open" 
171				data-wp-on--click="actions.openMenuOnClick"
172				data-wp-on--keydown="actions.handleMenuKeydown"
173			><svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 7.5h16v1.5H4z"></path><path d="M4 15h16v1.5H4z"></path></svg></button>
174				<div class="wp-block-navigation__responsive-container has-text-color has-contrast-color has-background has-base-background-color"  id="modal-1" 
175				data-wp-class--has-modal-open="state.isMenuOpen"
176				data-wp-class--is-menu-open="state.isMenuOpen"
177				data-wp-watch="callbacks.initMenu"
178				data-wp-on--keydown="actions.handleMenuKeydown"
179				data-wp-on--focusout="actions.handleMenuFocusout"
180				tabindex="-1"
181			>
182					<div class="wp-block-navigation__responsive-close" tabindex="-1">
183						<div class="wp-block-navigation__responsive-dialog" 
184				data-wp-bind--aria-modal="state.ariaModal"
185				data-wp-bind--aria-label="state.ariaLabel"
186				data-wp-bind--role="state.roleAttribute"
187			>
188							<button aria-label="Close menu" class="wp-block-navigation__responsive-container-close" 
189				data-wp-on--click="actions.closeMenuOnClick"
190			><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false"><path d="m13.06 12 6.47-6.47-1.06-1.06L12 10.94 5.53 4.47 4.47 5.53 10.94 12l-6.47 6.47 1.06 1.06L12 13.06l6.47 6.47 1.06-1.06L13.06 12Z"></path></svg></button>
191							<div class="wp-block-navigation__responsive-container-content" 
192				data-wp-watch="callbacks.focusFirstElement"
193			 id="modal-1-content">
194								
195								
196							</div>
197						</div>
198					</div>
199				</div></nav>
200			</div>
201			
202		</div>
203		
204	</div>
205	
206</div>
207
208
209</header>
210
211
212<main id="wp--skip-link--target" class="wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained" style="margin-top:var(--wp--preset--spacing--60)">
213	
214<h1 class="wp-block-heading has-text-align-left">Blog</h1>
215
216
217	
218<div class="wp-block-query alignfull is-layout-flow wp-block-query-is-layout-flow">
219	<ul class="alignfull wp-block-post-template is-layout-flow wp-block-post-template-is-layout-flow"><li class="wp-block-post post-26 post type-post status-publish format-standard hentry category-uncategorized tag-ai tag-it tag-privacy tag-search tag-selfhosted">
220		
221		<div class="wp-block-group alignfull has-global-padding is-layout-constrained wp-block-group-is-layout-constrained" style="padding-top:var(--wp--preset--spacing--60);padding-bottom:var(--wp--preset--spacing--60)">
222			
223			<h2 class="wp-block-post-title has-x-large-font-size"><a href="https://ko4bep.net/blog/index.php/2026/04/27/building-a-local-ai-search-page-with-searxng-and-ollama/" target="_self" >Building a Local AI Search Page with SearXNG and Ollama</a></h2>
224			<div class="entry-content alignfull wp-block-post-content has-medium-font-size has-global-padding is-layout-constrained wp-block-post-content-is-layout-constrained">
225<h1 class="wp-block-heading"></h1>
226
227
228
229<p class="wp-block-paragraph">Most search engines now bolt an AI answer box onto the top of the results page. That can be useful, but it also means your query and whatever the model does with it are happening on somebody else’s infrastructure.</p>
230
231
232
233<p class="wp-block-paragraph">This project builds the same basic workflow locally:</p>
234
235
236
237<ul class="wp-block-list">
238<li>SearXNG handles normal web search.</li>
239
240
241
242<li>Ollama runs a local model.</li>
243
244
245
246<li>A tiny Flask wrapper shows search results immediately.</li>
247
248
249
250<li>AI answers are optional. You check a box when you want them.</li>
251
252
253
254<li>Apache or other reverse proxy can publish the whole thing under <code>/search/</code> on an existing site.</li>
255</ul>
256
257
258
259<h2 class="wp-block-heading">What the final setup looks like</h2>
260
261
262
263<p class="wp-block-paragraph">The public page is:</p>
264
265
266
267<pre class="wp-block-code"><code>https:&#47;&#47;YOUR_DOMAIN/search/</code></pre>
268
269
270
271<p class="wp-block-paragraph">The local services are:</p>
272
273
274
275<pre class="wp-block-code"><code>SearXNG:   http://127.0.0.1:8080
276Ollama:    http://127.0.0.1:11434
277AI search: http://0.0.0.0:5001</code></pre>
278
279
280
281<p class="wp-block-paragraph">Of course, you can also use subdomains instead of directory based; I started using directories ages ago and have too much momentum to care about changing now.</p>
282
283
284
285<p class="wp-block-paragraph">Not every search needs a summary. Sometimes you just want results. The browser hits the AI search wrapper which loads normal SearXNG results and only calls Ollama when the user requests it.</p>
286
287
288
289<h2 class="wp-block-heading">Assumptions</h2>
290
291
292
293<p class="wp-block-paragraph">This guide is based on my experience setting this up on my own rig. It should apply broadly to current Ubuntu and derivatives, possibly with some tinkering:</p>
294
295
296
297<ul class="wp-block-list">
298<li>Ubuntu 25.10 or close enough.</li>
299
300
301
302<li>Docker Engine and Compose v2.</li>
303
304
305
306<li>Apache 2.4 as the reverse proxy (used in this doc, can be easily adapted for other RPs)</li>
307
308
309
310<li>A machine that can run Ollama locally (my machine for reference: Ryzen9 3900X, 128GiB RAM, NVIDIA 4060Ti 16GiB VRAM).</li>
311
312
313
314<li>A reverse proxy path of <code>/search/</code>.</li>
315</ul>
316
317
318
319<p class="wp-block-paragraph">The commands use <code>/opt/ai-search</code>. Change that path if you want, but don’t scatter the files around. Future-you will hate present-you.</p>
320
321
322
323<h2 class="wp-block-heading">Install Docker from the Docker repository</h2>
324
325
326
327<p class="wp-block-paragraph">Ubuntu’s Docker packages and Docker’s official packages can conflict. Pick one lane. I use Docker’s repository here.</p>
328
329
330
331<pre class="wp-block-code"><code>sudo apt update
332sudo apt install -y ca-certificates curl gnupg apache2
333
334sudo install -m 0755 -d /etc/apt/keyrings
335sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
336  -o /etc/apt/keyrings/docker.asc
337sudo chmod a+r /etc/apt/keyrings/docker.asc
338
339echo \
340"deb &#91;arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
341https://download.docker.com/linux/ubuntu \
342$(. /etc/os-release &amp;&amp; echo "$VERSION_CODENAME") stable" | \
343sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null
344
345sudo apt update
346sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
347sudo systemctl enable --now docker</code></pre>
348
349
350
351<p class="wp-block-paragraph">Check it:</p>
352
353
354
355<pre class="wp-block-code"><code>sudo docker version
356docker compose version</code></pre>
357
358
359
360<h2 class="wp-block-heading">Create the project directory</h2>
361
362
363
364<pre class="wp-block-code"><code>sudo mkdir -p /opt/ai-search/{searxng,ollama}
365sudo chown -R "$USER:$USER" /opt/ai-search
366cd /opt/ai-search</code></pre>
367
368
369
370<h2 class="wp-block-heading">Docker Compose: SearXNG and Ollama</h2>
371
372
373
374<p class="wp-block-paragraph">This setup uses Docker host networking. That is deliberate.</p>
375
376
377
378<p class="wp-block-paragraph">I use a userspace VPN and systemwide TailScale on the same rig (getting my money&#8217;s worth out of a gaming machine when I&#8217;m not gaming). Docker bridge networking and Docker’s embedded DNS can get weird and create frustrating, time wasting conflicts. Host networking removes that whole layer for this project. The tradeoff is that ports bind directly on the host, so do not run this blindly on a 
378shared machine.</p>
379
380
381
382<p class="wp-block-paragraph">Create <code>/opt/ai-search/docker-compose.yml</code>:</p>
383
384
385
386<pre class="wp-block-code"><code>services:
387  searxng:
388    image: docker.io/searxng/searxng:latest
389    container_name: searxng
390    restart: unless-stopped
391    network_mode: host
392    volumes:
393      - ./searxng:/etc/searxng
394
395  ollama:
396    image: docker.io/ollama/ollama:latest
397    container_name: ollama
398    restart: unless-stopped
399    network_mode: host
400    volumes:
401      - ./ollama:/root/.ollama</code></pre>
402
403
404
405<p class="wp-block-paragraph">Start it:</p>
406
407
408
409<pre class="wp-block-code"><code>cd /opt/ai-search
410sudo docker compose up -d</code></pre>
411
412
413
414<h2 class="wp-block-heading">SearXNG settings</h2>
415
416
417
418<p class="wp-block-paragraph">Create <code>/opt/ai-search/searxng/settings.yml</code>:</p>
419
420
421
422<pre class="wp-block-code"><code>use_default_settings: true
423
424general:
425  debug: false
426  instance_name: "Search"
427
428search:
429  safe_search: 0
430  autocomplete: duckduckgo
431  default_lang: en-US
432  formats:
433    - html
434    - json
435
436server:
437  secret_key: "CHANGE_THIS_TO_A_LONG_RANDOM_VALUE"
438  base_url: http://127.0.0.1:8080/
439  limiter: false
440  image_proxy: false
441  method: GET
442
443ui:
444  infinite_scroll: false
445  query_in_title: true
446  results_on_new_tab: true
447
448plugins:
449  searx.plugins.hostnames.SXNGPlugin:
450    active: true
451  searx.plugins.tracker_url_remover.SXNGPlugin:
452    active: true
453  searx.plugins.calculator.SXNGPlugin:
454    active: true
455
456engines:
457  - name: brave
458    disabled: true
459  - name: karmasearch
460    disabled: true
461  - name: karmasearch videos
462    disabled: true
463  - name: mojeek
464    disabled: true
465  - name: yahoo
466    disabled: true</code></pre>
467
468
469
470<p class="wp-block-paragraph">Generate a real secret key:</p>
471
472
473
474<pre class="wp-block-code"><code>python3 - &lt;&lt;'PY'
475import secrets
476print(secrets.token_hex(32))
477PY</code></pre>
478
479
480
481<p class="wp-block-paragraph">Replace <code>CHANGE_THIS_TO_A_LONG_RANDOM_VALUE</code> with the generated value.</p>
482
483
484
485<p class="wp-block-paragraph">Restart SearXNG:</p>
486
487
488
489<pre class="wp-block-code"><code>cd /opt/ai-search
490sudo docker compose restart searxng
491curl -I http://127.0.0.1:8080</code></pre>
492
493
494
495<p class="wp-block-paragraph">SearXNG must have JSON enabled because the AI wrapper reads search results through <code>/search?q=...&amp;format=json</code>.</p>
496
497
498
499<h2 class="wp-block-heading">Pull a local model with Ollama</h2>
500
501
502
503<p class="wp-block-paragraph">This uses Qwen 2.5 7B because it is small enough for normal local hardware and good enough for short search summaries.<br>f you use a</p>
504
505
506
507<pre class="wp-block-code"><code>curl http://127.0.0.1:11434/api/pull \
508  -d '{"model":"qwen2.5:7b","stream":false}'
509
510curl http://127.0.0.1:11434/api/tags</code></pre>
511
512
513
514<p class="wp-block-paragraph">Test generation:</p>
515
516
517
518<pre class="wp-block-code"><code>curl http://127.0.0.1:11434/api/generate \
519  -d '{"model":"qwen2.5:7b","prompt":"Reply with exactly: model working","stream":false}'</code></pre>
520
521
522
523<h2 class="wp-block-heading">Install Python dependencies</h2>
524
525
526
527<p class="wp-block-paragraph">The wrapper is a small Flask app. For the service, use Gunicorn instead of Flask’s development server.</p>
528
529
530
531<pre class="wp-block-code"><code>sudo apt update
532sudo apt install -y python3-flask python3-requests gunicorn</code></pre>
533
534
535
536<h2 class="wp-block-heading">The AI search wrapper</h2>
537
538
539
540<p class="wp-block-paragraph">Create <code>/opt/ai-search/ai_search_app.py</code>:</p>
541
542
543
544<pre class="wp-block-code"><code>from flask import Flask, request
545import html
546import requests
547
548app = Flask(__name__)
549
550SEARX_UI = "http://127.0.0.1:8080"
551SEARX_API = "http://127.0.0.1:8080/search"
552OLLAMA_API = "http://127.0.0.1:11434/api/generate"
553MODEL = "qwen2.5:7b"
554
555STYLE = """
556body {
557  margin: 0;
558  font-family: system-ui, sans-serif;
559  background: #111;
560  color: #eee;
561}
562.topbar {
563  padding: 12px;
564  background: #181818;
565  border-bottom: 1px solid #333;
566}
567form {
568  display: flex;
569  gap: 8px;
570}
571input&#91;type="text"] {
572  flex: 1;
573  padding: 10px;
574  font-size: 16px;
575}
576button {
577  padding: 10px 16px;
578  font-size: 16px;
579}
580.ai-box {
581  padding: 14px;
582  margin: 12px;
583  border: 1px solid #444;
584  border-radius: 8px;
585  background: #1b1b1b;
586}
587.ai-loading {
588  opacity: 0.75;
589}
590#raw-results {
591  background: #fff;
592  color: #111;
593  padding: 12px;
594}
595#raw-results a {
596  color: #0645ad;
597}
598"""
599
600SEARX_CSS = '&lt;link rel="stylesheet" href="/search/raw/static/themes/simple/sxng-ltr.min.css" type="text/css"&gt;'
601
602@app.route("/")
603def index():
604    q = request.args.get("q", "").strip()
605    ai_enabled = request.args.get("ai") == "1"
606    q_html = html.escape(q)
607    checked = "checked" if ai_enabled else ""
608
609    if not q:
610        return f"""
611&lt;html&gt;
612&lt;head&gt;&lt;title&gt;AI Search&lt;/title&gt;{SEARX_CSS}&lt;style&gt;{STYLE}&lt;/style&gt;&lt;/head&gt;
613&lt;body&gt;
614  &lt;div class="topbar"&gt;
615    &lt;form action="/search/" method="get"&gt;
616      &lt;input type="text" name="q" autofocus placeholder="Search..." /&gt;
617      &lt;label style="display:flex;align-items:center;gap:6px"&gt;
618        &lt;input type="checkbox" name="ai" value="1"&gt;
619        include AI
620      &lt;/label&gt;
621      &lt;button type="submit"&gt;Search&lt;/button&gt;
622    &lt;/form&gt;
623  &lt;/div&gt;
624&lt;/body&gt;
625&lt;/html&gt;
626"""
627
628    quoted_q = requests.utils.quote(q)
629    raw_url = "/search/raw/search?q=" + quoted_q
630
631    if ai_enabled:
632        ai_block = """
633  &lt;div id="ai" class="ai-box ai-loading"&gt;
634    &lt;b&gt;AI Answer&lt;/b&gt;&lt;br&gt;&lt;br&gt;
635    Working...
636  &lt;/div&gt;
637"""
638        ai_script = f"""
639  &lt;script&gt;
640    fetch("/search/answer?q=" + encodeURIComponent({q!r}))
641      .then(r =&gt; r.text())
642      .then(t =&gt; {{
643        document.getElementById("ai").classList.remove("ai-loading");
644        document.getElementById("ai").innerHTML = t;
645      }})
646      .catch(() =&gt; {{
647        document.getElementById("ai").innerHTML = "&lt;b&gt;AI Answer&lt;/b&gt;&lt;br&gt;&lt;br&gt;Unavailable.";
648      }});
649  &lt;/script&gt;
650"""
651    else:
652        ai_block = f"""
653  &lt;div class="ai-box"&gt;
654    &lt;b&gt;AI Answer&lt;/b&gt;&lt;br&gt;&lt;br&gt;
655    &lt;a style="color:#9cf" href="/search/?q={quoted_q}&amp;ai=1"&gt;Generate AI summary&lt;/a&gt;
656  &lt;/div&gt;
657"""
658        ai_script = ""
659
660    return f"""
661&lt;html&gt;
662&lt;head&gt;&lt;title&gt;{q_html} - AI Search&lt;/title&gt;{SEARX_CSS}&lt;style&gt;{STYLE}&lt;/style&gt;&lt;/head&gt;
663&lt;body&gt;
664  &lt;div class="topbar"&gt;
665    &lt;form action="/search/" method="get"&gt;
666      &lt;input type="text" name="q" value="{q_html}" /&gt;
667      &lt;label style="display:flex;align-items:center;gap:6px"&gt;
668        &lt;input type="checkbox" name="ai" value="1" {checked}&gt;
669        include AI
670      &lt;/label&gt;
671      &lt;button type="submit"&gt;Search&lt;/button&gt;
672      &lt;a style="color:#9cf;padding:10px" href="{raw_url}" target="_blank"&gt;Open raw SearXNG&lt;/a&gt;
673    &lt;/form&gt;
674  &lt;/div&gt;
675
676{ai_block}
677
678  &lt;div id="raw-results"&gt;Loading search results...&lt;/div&gt;
679
680  &lt;script&gt;
681    fetch("/search/raw-html?q=" + encodeURIComponent({q!r}))
682      .then(r =&gt; r.text())
683      .then(t =&gt; {{
684        document.getElementById("raw-results").innerHTML = t;
685      }})
686      .catch(() =&gt; {{
687        document.getElementById("raw-results").innerHTML = "Search results unavailable.";
688      }});
689  &lt;/script&gt;
690
691{ai_script}
692&lt;/body&gt;
693&lt;/html&gt;
694"""
695
696@app.route("/raw-html")
697def raw_html():
698    q = request.args.get("q", "").strip()
699    if not q:
700        return ""
701
702    r = requests.get(SEARX_UI + "/search", params={"q": q}, timeout=45)
703    r.raise_for_status()
704    page = r.text
705
706    start = page.find('&lt;main id="main_results"')
707    if start == -1:
708        return page
709
710    end = page.rfind("&lt;/main&gt;")
711    if end == -1:
712        return page&#91;start:]
713
714    return page&#91;start:end + len("&lt;/main&gt;")]
715
716@app.route("/answer")
717def answer():
718    q = request.args.get("q", "").strip()
719    if not q:
720        return ""
721
722    try:
723        sx = requests.get(SEARX_API, params={"q": q, "format": "json"}, timeout=30)
724        sx.raise_for_status()
725        data = sx.json()
726
727        lines = &#91;]
728        for r in data.get("results", &#91;])&#91;:6]:
729            title = r.get("title", "")
730            content = r.get("content", "")
731            url = r.get("url", "")
732            if title or content:
733                lines.append(f"{title}\n{content}\n{url}")
734
735        prompt = (
736            "User query:\n" + q +
737            "\n\nSearch results:\n" + "\n\n".join(lines) +
738            "\n\nWrite a concise answer in 3 bullet points. "
739            "Use only the provided search results. "
740            "If the results are weak or unrelated, say so."
741        )
742
743        ol = requests.post(
744            OLLAMA_API,
745            json={"model": MODEL, "prompt": prompt, "stream": False},
746            timeout=120,
747        )
748        ol.raise_for_status()
749
750        text = ol.json().get("response", "").strip()
751        safe = html.escape(text).replace("\n", "&lt;br&gt;")
752        return "&lt;b&gt;AI Answer&lt;/b&gt;&lt;br&gt;&lt;br&gt;" + safe
753
754    except Exception as e:
755        return "&lt;b&gt;AI Answer&lt;/b&gt;&lt;br&gt;&lt;br&gt;Unavailable: " + html.escape(str(e))
756
757if __name__ == "__main__":
758    app.run(host="0.0.0.0", port=5001)</code></pre>
759
760
761
762<p class="wp-block-paragraph">Two small design choices are doing a lot of work here:</p>
763
764
765
766<ul class="wp-block-list">
767<li>The search results load first.</li>
768
769
770
771<li>AI only runs when <code>ai=1</code> is present.</li>
772</ul>
773
774
775
776<p class="wp-block-paragraph">That keeps normal searches quick.</p>
777
778
779
780<h2 class="wp-block-heading">Run it as a service</h2>
781
782
783
784<p class="wp-block-paragraph">Create <code>/etc/systemd/system/ai-search.service</code>:</p>
785
786
787
788<pre class="wp-block-code"><code>&#91;Unit]
789Description=Local AI Search Wrapper
790After=network.target docker.service
791Wants=docker.service
792
793&#91;Service]
794Type=simple
795User=YOUR_LOCAL_USER
796WorkingDirectory=/opt/ai-search
797ExecStart=/usr/bin/gunicorn -w 2 -b 0.0.0.0:5001 ai_search_app:app
798Restart=always
799RestartSec=3
800
801&#91;Install]
802WantedBy=multi-user.target</code></pre>
803
804
805
806<p class="wp-block-paragraph">Set your local username:</p>
807
808
809
810<pre class="wp-block-code"><code>sudo sed -i "s/User=YOUR_LOCAL_USER/User=$USER/" /etc/systemd/system/ai-search.service
811sudo systemctl daemon-reload
812sudo systemctl enable --now ai-search.service</code></pre>
813
814
815
816<p class="wp-block-paragraph">Test locally:</p>
817
818
819
820<pre class="wp-block-code"><code>curl -I http://127.0.0.1:5001
821curl -L "http://127.0.0.1:5001/?q=linux%20firewall" | head
822curl -L "http://127.0.0.1:5001/?q=linux%20firewall&amp;ai=1" | head</code></pre>
823
824
825
826<h2 class="wp-block-heading">Apache reverse proxy</h2>
827
828
829
830<p class="wp-block-paragraph">This example publishes the wrapper at <code>/search/</code> and raw SearXNG at <code>/search/raw/</code>.</p>
831
832
833
834<p class="wp-block-paragraph">Set the backend IP before using the config:</p>
835
836
837
838<pre class="wp-block-code"><code>AI_SEARCH_HOST="192.168.1.50"</code></pre>
839
840
841
842<p class="wp-block-paragraph">Use the actual LAN or VPN IP of the machine running the AI search service.</p>
843
844
845
846<p class="wp-block-paragraph">Put this inside your existing Apache TLS vhost:</p>
847
848
849
850<pre class="wp-block-code"><code># Local AI Search
851# Public paths:
852#   /search/      -&gt; AI wrapper
853#   /search/raw/  -&gt; raw SearXNG assets and search page
854
855RedirectMatch 308 ^/search$ /search/
856
857ProxyPreserveHost On
858ProxyRequests Off
859
860# RAW SEARXNG MUST COME FIRST
861ProxyPass        /search/raw/ http://AI_SEARCH_HOST:8080/
862ProxyPassReverse /search/raw/ http://AI_SEARCH_HOST:8080/
863
864# AI WRAPPER SECOND
865ProxyPass        /search/ http://AI_SEARCH_HOST:5001/
866ProxyPassReverse /search/ http://AI_SEARCH_HOST:5001/
867
868&lt;Location /search/raw/&gt;
869    Require all granted
870
871    RequestHeader set X-Forwarded-Proto "https"
872    RequestHeader set X-Forwarded-Host "YOUR_DOMAIN"
873    RequestHeader set X-Forwarded-Prefix "/search/raw"
874    RequestHeader set X-Scheme "https"
875    RequestHeader set X-Script-Name "/search/raw"
876
877    RequestHeader set X-Real-IP %{REMOTE_ADDR}s
878    RequestHeader append X-Forwarded-For %{REMOTE_ADDR}s
879&lt;/Location&gt;
880
881&lt;Location /search/&gt;
882    Require all granted
883
884    RequestHeader set X-Forwarded-Proto "https"
885    RequestHeader set X-Forwarded-Host "YOUR_DOMAIN"
886    RequestHeader set X-Forwarded-Prefix "/search"
887    RequestHeader set X-Scheme "https"
888    RequestHeader set X-Script-Name "/search"
889
890    RequestHeader set X-Real-IP %{REMOTE_ADDR}s
891    RequestHeader append X-Forwarded-For %{REMOTE_ADDR}s
892
893    SetEnvIf Request_URI "^/search/" dontlog
894&lt;/Location&gt;</code></pre>
895
896
897
898<p class="wp-block-paragraph">Replace <code>AI_SEARCH_HOST</code> and <code>YOUR_DOMAIN</code> before reloading Apache.</p>
899
900
901
902<p class="wp-block-paragraph">The order matters. <code>/search/raw/</code> must come before <code>/search/</code>, or Apache will send raw SearXNG requests to the wrapper.</p>
903
904
905
906<p class="wp-block-paragraph">Enable modules and reload:</p>
907
908
909
910<pre class="wp-block-code"><code>sudo a2enmod proxy proxy_http headers rewrite ssl
911sudo apache2ctl configtest
912sudo systemctl reload apache2</code></pre>
913
914
915
916<h2 class="wp-block-heading">Test the final page</h2>
917
918
919
920<p class="wp-block-paragraph">Open:</p>
921
922
923
924<pre class="wp-block-code"><code>https:&#47;&#47;YOUR_DOMAIN/search/</code></pre>
925
926
927
928<p class="wp-block-paragraph">Search normally. Results should load without calling the model.</p>
929
930
931
932<p class="wp-block-paragraph">Then check <code>include AI</code> and search again. Results should still load first, and the AI answer should appear after a few seconds.</p>
933
934
935
936<h2 class="wp-block-heading">Notes from the build</h2>
937
938
939
940<p class="wp-block-paragraph">Do not start by hacking SearXNG plugins. That sounds cleaner than 
940it is. The current SearXNG plugin system expects proper importable Python modules and fully qualified plugin class names. A wrapper avoids tying your project to SearXNG internals.</p>
941
942
943
944<p class="wp-block-paragraph">Do not put <code>127.0.0.1</code> URLs in HTML that will be loaded by another machine. The user’s browser interprets <code>127.0.0.1</code> as the user’s own computer, not your server. Use public paths like <code>/search/raw/search?...</code> and let Apache proxy them.</p>
945
946
947
948<p class="wp-block-paragraph">Do not run AI on every query unless you really want the latency (or you&#8217;re keeping warm in winter).</p>
949
950
951
952<p class="wp-block-paragraph">If you use a VPN and Docker networking explodes, try host networking for this stack. It is blunt, but it avoids a lot of route and DNS drama.</p>
953
954
955
956<h2 class="wp-block-heading">Useful references</h2>
957
958
959
960<ul class="wp-block-list">
961<li>SearXNG Search API: https://docs.searxng.org/dev/search_api.html</li>
962
963
964
965<li>SearXNG JSON formats need to be enabled in <code>settings.yml</code>: https://docs.searxng.org/dev/search_api.html</li>
966
967
968
969<li>Ollama generate API: https://ollama.readthedocs.io/en/api/</li>
970
971
972
973<li>Ollama pull API: https://docs.ollama.com/api/pull</li>
974
975
976
977<li>Docker host networking: https://docs.docker.com/engine/network/drivers/host/</li>
978
979
980
981<li>Apache reverse proxy guide: https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html</li>
982
983
984
985<li>Apache mod_proxy docs: https://httpd.apache.org/docs/current/mod/mod_proxy.html</li>
986</ul>
987</div>
988			<div style="margin-top:var(--wp--preset--spacing--40)" class="wp-block-post-date has-small-font-size"><a href="https://ko4bep.net/blog/index.php/2026/04/27/building-a-local-ai-search-page-with-searxng-and-ollama/"><time datetime="2026-04-27T18:53:02+00:00">April 27, 2026</time></a></div>
989		</div>
990		
991	</li><li class="wp-block-post post-10 post type-post status-publish format-standard hentry category-uncategorized tag-docker tag-it tag-rants tag-security">
992		
993		<div class="wp-block-group alignfull has-global-padding is-layout-constrained wp-block-group-is-layout-constrained" style="padding-top:var(--wp--preset--spacing--60);padding-bottom:var(--wp--preset--spacing--60)">
994			
995			<h2 class="wp-block-post-title has-x-large-font-size"><a href="https://ko4bep.net/blog/index.php/2025/11/22/why-docker-is-a-pain-in-the-ass/" target="_self" >Why Docker Is a Pain-in-the-Ass</a></h2>
996			<div class="entry-content alignfull wp-block-post-content has-medium-font-size has-global-padding is-layout-constrained wp-block-post-content-is-layout-constrained">
997<h2 class="wp-block-heading">(And Why You’re Totally Right to Hate It)</h2>
998
999
1000
1001<p class="wp-block-paragraph">If you’re dealing with containers and still have a shred of sanity, here’s the brutal truth: Docker is less of a magical “safe sandbox” and more of a <strong>convenience-bait with a liability tag</strong>. Let’s tear down the hype and get real about what this thing really brings — so you (and your users) can make an informed decision instead of blindly following the “containerize everything” evangelists.</p>
1002
1003
1004
1005<hr class="wp-block-separator has-alpha-channel-opacity"/>
1006
1007
1008
1009<h2 class="wp-block-heading">1. The Broken Promise of “Isolation”</h2>
1010
1011
1012
1013<p class="wp-block-paragraph">Docker likes to sell containers like they’re neat little self-contained sandboxes. Reality? They share the host kernel.<br>For instance: the “Leaky Vessels” vulnerabilities demonstrate how a container could break out and compromise the host. :contentReference[oaicite:1]{index=1}<br>One recent critical bug, CVE-2025-9074 (CVSS 9.3), allowed a container on Docker Desktop to access the Engine API and launch new containers—<em>without requiring the Docker socket to be mounted</em>. That’s full host compromise by design failure. :contentReference[oaicite:2]{index=2}<br>If you’re relying on “containers = safe” you’re working on sand.</p>
1014
1015
1016
1017<hr class="wp-block-separator has-alpha-channel-opacity"/>
1018
1019
1020
1021<h2 class="wp-block-heading">2. Defaults Built for Convenience, Not Security</h2>
1022
1023
1024
1025<p class="wp-block-paragraph">
1025Running as root inside the container? Host volumes mounted indiscriminately? Docker socket (<code>/var/run/docker.sock</code>) handed to untrusted containers? These are not fringe cases—they’re default modes of operation for many users.<br>When you deploy tools for novice-to-intermediate users, deploying them inside Docker <em>without</em> being explicit about this is asking for trouble.<br>Simply put: if you skip the hardening, you inherited a risk.<br>See the “Attacker’s Tactics and Techniques in Unsecured Docker Daemons” paper: unsecured daemons = bad times. :contentReference[oaicite:3]{index=3}</p>
1026
1027
1028
1029<hr class="wp-block-separator has-alpha-channel-opacity"/>
1030
1031
1032
1033<h2 class="wp-block-heading">3. The API That Acts Like It’s Open Season</h2>
1034
1035
1036
1037<p class="wp-block-paragraph">One of Docker’s biggest sins: remote Docker API (especially on port 2375) exposed without authentication. Attackers have used this to spin up containers, mount host directories, drop cryptominers.<br>A new campaign cited by Akamai (Aug/2025) shows this in action: malicious containers created via exposed API, mounting host filesystem at will. :contentReference[oaicite:5]{index=5}<br>If your deployment gives users “pull-run-go” with no API audit, you’re handing them an open door with a welcome mat.</p>
1038
1039
1040
1041<hr class="wp-block-separator has-alpha-channel-opacity"/>
1042
1043
1044
1045<h2 class="wp-block-heading">4. The Supply-Chain Sucker-Punch</h2>
1046
1047
1048
1049<p class="wp-block-paragraph">You pull an image from Docker Hub and think “cool, containerized, done.” Wrong.<br>A study on base-image vulnerabilities found that many images inherit flawed foundations: “Well Begun is Half Done: An Empirical Study of Exploitability &amp; Impact of Base-Image Vulnerabilities”. :contentReference[oaicite:6]{index=6}<br>Even worse: “gh0stEdit” research shows you can tamper with a signed image without invalidating the signature—undetected malicious layers. :contentReference[oaicite:7]{index=7}<br>“Trust but verify” should be your mantra. With Docker, you might not even be getting that.</p>
1050
1051
1052
1053<hr class="wp-block-separator has-alpha-channel-opacity"/>
1054
1055
1056
1057<h2 class="wp-block-heading">5. Bloat &amp; Time-Bomb of Maintenance</h2>
1058
1059
1060
1061<p class="wp-block-paragraph">Because many Dockerfiles are written for speed and convenience (just throw everything in), images inflate in size, dependencies pile up, attack surface grows.<br>If you’re building a security tool, you don’t want a 1 GB monster image filled with unnecessary libs-and-bins. One paper categorised bad base image practices as major exploit enablers. :contentReference[oaicite:8]{index=8}<br>Also: more tooling = more attack surface (engine, desktop, compose, orchestrator). If you aren’t vigilant, you inherit the overhead.</p>
1062
1063
1064
1065<hr class="wp-block-separator has-alpha-channel-opacity"/>
1066
1067
1068
1069<h2 class="wp-block-heading">6. Vulnerabilities Keep Piling Up</h2>
1070
1071
1072
1073<p class="wp-block-paragraph">Containers are not invulnerable. They carry their own risks.<br>Docker’s own security advisory shows a string of critical vulnerabilities: e.g., CVE-2025-9074 (Docker Desktop host compromise), CVE-2025-9164 (DLL hijack in installer) … :contentReference[oaicite:9]{index=9}<br>And the runtime level: new runC vulnerabilities allow container escape toward host via symlink or race conditions. :contentReference[oaicite:10]{index=10}<br>When your stack says “containers = safe by default” — misleading is a nice word.</p>
1074
1075
1076
1077<hr class="wp-block-separator has-alpha-channel-opacity"/>
1078
1079
1080
1081<h2 class="wp-block-heading">7. Shared Kernel + Lazy Admins = Disaster Potential</h2>
1082
1083
1084
1085<p class="wp-block-paragraph">The fundamental architectural issue: containers share the host OS kernel. A host-kernel exploit or privileged container → host takeover.<br>From French Wikipedia (yay) on Docker security:</p>
1086
1087
1088
1089<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
1090<p class="wp-block-paragraph">“Les conteneurs Linux n’ont pas été conçus comme un mécanisme de sécurité permettant d’isoler les conteneurs non fiables … un exploit donnant des privilèges root complets à un conteneur peut permettre à un attaquant de percer ce conteneur et de compromettre l’hôte.” :contentReference[oaicite:11]{index=11}<br>If your user base isn’t firmly disciplined, you’re basically trusting that “everything goes right” instead of designing for “what happens when someone screws up”.</p>
1091</blockquote>
1092
1093
1094
1095<hr class="wp-block-separator has-alpha-channel-opacity"/>
1096
1097
1098
1099<h2 class="wp-block-heading">8. Real Incidents: People <em>Got Hurt</em></h2>
1100
1101
1102
1103<ul class="wp-block-list">
1104<li>A campaign by Akamai (Sep 2025) targeted exposed Docker APIs and installed cryptominers/backdoors. :contentReference[oaicite:12]{index=12}</li>
1105
1106
1107
1108<li>Another research (Datadog, Jun 2024) observed containers spun on exposed Docker daemons for cryptojacking. :contentReference[oaicite:13]{index=13}</li>
1109
1110
1111
1112<li>
1112Older but relevant: Trend Micro (2019) found images on Docker Hub used for mining via exposed APIs. :contentReference[oaicite:14]{index=14}<br>These aren’t “maybe could happen” — they did happen. Your users should know that.</li>
1113</ul>
1114
1115
1116
1117<hr class="wp-block-separator has-alpha-channel-opacity"/>
1118
1119
1120
1121<h2 class="wp-block-heading">9. Tooling Complexity Hides Risk, Not Reduces It</h2>
1122
1123
1124
1125<p class="wp-block-paragraph">Using Docker often means: engine, desktop GUI, CLI, compose files, registries, networks, volumes, metadata. Each layer invites misconfiguration.<br>If you target novice-to-intermediate users and deploy via Docker without providing <em>explicit training</em> or guardrails, you’re stacking the odds toward error.<br>Again: convenience ≠ safety.</p>
1126
1127
1128
1129<hr class="wp-block-separator has-alpha-channel-opacity"/>
1130
1131
1132
1133<h2 class="wp-block-heading">10. The “Easy” Narrative Is Misleading</h2>
1134
1135
1136
1137<p class="wp-block-paragraph">Marketing says: “Build once, run anywhere, container magic”. Reality:<br>You still need to secure the daemon, lock down APIs, verify images, limit capabilities, monitor runtime, isolate networks, patch base images.<br>When you skip these, your “safe container” becomes your weakest link.</p>
1138
1139
1140
1141<hr class="wp-block-separator has-alpha-channel-opacity"/>
1142
1143
1144
1145<h2 class="wp-block-heading">Final Word</h2>
1146
1147
1148
1149<p class="wp-block-paragraph">Docker: convenience-bait with a hidden price-tag.<br>Isolation? weaker than you think.<br>Defaults? optimized for “works fast”, not “secures tight”.<br>Image ecosystem? messy.<br>Infrastructure burden? real.<br>If you’re sharing applications with the world, </p>
1150
1151
1152
1153<hr class="wp-block-separator has-alpha-channel-opacity"/>
1154
1155
1156
1157<h2 class="wp-block-heading">References</h2>
1158
1159
1160
1161<ol class="wp-block-list">
1162<li>Docker Desktop 4.44.3: Important Container Escape Vuln. LinuxSecurity. (Aug 26 2025). <a href="https://linuxsecurity.com/news/security-vulnerabilities/docker-desktop-4-44-3-security-update">https://linuxsecurity.com/news/security-vulnerabilities/docker-desktop-4-44-3-security-update</a> :contentReference[oaicite:15]{index=15}</li>
1163
1164
1165
1166<li>Off Your Docker: Exposed APIs Are Targeted in New Malware Strain. Akamai Hunt Team. (Sep 8 2025). <a href="https://www.akamai.com/blog/security-research/new-malware-targeting-docker-apis-akamai-hunt">https://www.akamai.com/blog/security-research/new-malware-targeting-docker-apis-akamai-hunt</a> :contentReference[oaicite:16]{index=16}</li>
1167
1168
1169
1170<li>Docker Fixes CVE-2025-9074, Critical Container Escape Vulnerability (CVSS 9.3). The Hacker News. (Aug 25 2025). <a href="https://thehackernews.com/2025/08/docker-fixes-cve-2025-9074-critical.html">https://thehackernews.com/2025/08/docker-fixes-cve-2025-9074-critical.html</a> :contentReference[oaicite:17]{index=17}</li>
1171
1172
1173
1174<li>Attackers deploying new tactics in campaign targeting exposed Docker APIs. Datadog Security Labs. (Jun 13 2024). <a href="https://securitylabs.datadoghq.com/articles/attackers-deploying-new-tactics-in-campaign-targeting-exposed-docker-apis/">https://securitylabs.datadoghq.com/articles/attackers-deploying-new-tactics-in-campaign-targeting-exposed-docker-apis/</a> :contentReference[oaicite:18]{index=18}</li>
1175
1176
1177
1178<li>Container Escape: New Vulnerabilities Affecting Docker. Palo-Alto Networks blog. (Feb 5 2024). <a href="https://www.paloaltonetworks.com/blog/cloud-security/leaky-vessels-vulnerabilities-container-escape/">https://www.paloaltonetworks.com/blog/cloud-security/leaky-vessels-vulnerabilities-container-escape/</a> :contentReference[oaicite:19]{index=19}</li>
1179
1180
1181
1182<li>Infected Containers Target Docker via Exposed APIs. Trend Micro. (May 30 2019). <a href="https://www.trendmicro.com/en_us/research/19/e/infected-cryptocurrency-mining-containers-target-docker-hosts-with-exposed-apis-use-shodan-to-find-additional-victims.html">https://www.trendmicro.com/en_us/research/19/e/infected-cryptocurrency-mining-containers-target-docker-hosts-with-exposed-apis-use-shodan-to-find-additional-victims.html</a> :contentReference[oaicite:20]{index=20}</li>
1183
1184
1185
1186<li>Well Begun Is Half Done: An Empirical Study of Exploitability &amp; Impact of Base-Image Vulnerabilities. Ul Haque et al. (Dec 21 2021). <a href="https://arxiv.org/abs/2112.12597">https://arxiv.org/abs/2112.12597</a> :contentReference[oaicite:21]{index=21}</li>
1187
1188
1189
1190<li>gh0stEdit: Exploiting Layer-Based Access Vulnerability Within Docker Container Images. Mills et al. (Jun 9 2025). <a href="https://arxiv.org/abs/2506.08218">https://arxiv.org/abs/2506.08218</a> :contentReference[oaicite:22]{index=22}</li>
1191
1192
1193
1194<li>Docker security announcements. Docker official docs. <a href="https://docs.docker.com/security/security-announcements/">https://docs.docker.com/security/security-announcements/</a> :contentReference[oaicite:23]{index=23}</li>
1195
1196
1197
1198<li>New runC Vulnerabilities Expose Docker and Kubernetes to Container Escape Attacks. ORCA/orca.security blog. (Nov 10 2025). <a href="https://orca.security/resources/blog/new-runc-vulnerabilities-allow-container-escape/">
1198https://orca.security/resources/blog/new-runc-vulnerabilities-allow-container-escape/</a> :contentReference[oaicite:24]{index=24}</li>
1199
1200
1201
1202<li>Vulnerable Docker Installations: Malware Attack Playground. Uptycs Threat Research. (May 2 2022). <a href="https://www.uptycs.com/blog/threat-research-report-team/vulnerable-docker-installations-are-a-playhouse-for-malware-attacks">https://www.uptycs.com/blog/threat-research-report-team/vulnerable-docker-installations-are-a-playhouse-for-malware-attacks</a> :contentReference[oaicite:25]{index=25}</li>
1203
1204
1205
1206<li>Attacker’s Tactics and Techniques in Unsecured Docker Daemons Revealed. Unit 42, Palo Alto Networks. (Jan 29 2020). <a href="https://unit42.paloaltonetworks.com/attackers-tactics-and-techniques-in-unsecured-docker-daemons-revealed/">https://unit42.paloaltonetworks.com/attackers-tactics-and-techniques-in-unsecured-docker-daemons-revealed/</a> :contentReference[oaicite:26]{index=26}</li>
1207</ol>
1208</div>
1209			<div style="margin-top:var(--wp--preset--spacing--40)" class="wp-block-post-date has-small-font-size"><a href="https://ko4bep.net/blog/index.php/2025/11/22/why-docker-is-a-pain-in-the-ass/"><time datetime="2025-11-22T16:16:02+00:00">November 22, 2025</time></a></div>
1210		</div>
1211		
1212	</li></ul>
1213	
1214	<div class="wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained" style="padding-top:var(--wp--preset--spacing--60);padding-bottom:var(--wp--preset--spacing--60)">
1215		
1216	</div>
1217	
1218	
1219	<div class="wp-block-group alignwide has-global-padding is-layout-constrained wp-block-group-is-layout-constrained">
1220		
1221	</div>
1222	
1223</div>
1224
1225
1226</main>
1227
1228
1229<footer class="wp-block-template-part">
1230<div class="wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained" style="padding-top:var(--wp--preset--spacing--60);padding-bottom:var(--wp--preset--spacing--50)">
1231	
1232	<div class="wp-block-group alignwide is-layout-flow wp-block-group-is-layout-flow">
1233		
1234
1235		
1236		<div class="wp-block-group alignfull is-content-justification-space-between is-layout-flex wp-container-core-group-is-layout-cf54d0a6 wp-block-group-is-layout-flex">
1237			
1238			<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-794e3cfa wp-block-columns-is-layout-flex">
1239				
1240				<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%"><h2 class="wp-block-site-title"><a href="https://ko4bep.net/blog" target="_self" rel="home" aria-current="page">KO4BEP :: Blog</a></h2>
1241
1242				<p class="wp-block-site-tagline">nonsense.</p>
1243				</div>
1244				
1245
1246				
1247				<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
1248					
1249					<div style="height:var(--wp--preset--spacing--40);width:0px" aria-hidden="true" class="wp-block-spacer"></div>
1250					
1251				</div>
1252				
1253			</div>
1254			
1255
1256			
1257			<div class="wp-block-group is-content-justification-space-between is-layout-flex wp-container-core-group-is-layout-bf19e0b3 wp-block-group-is-layout-flex">
1258				<nav class="is-vertical wp-block-navigation is-layout-flex wp-container-core-navigation-is-layout-831b2db5 wp-block-navigation-is-layout-flex" aria-label=" 2"><ul class="wp-block-navigation__container is-vertical wp-block-navigation"><li class="wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content"  href="#"><span class="wp-block-navigation-item__label">Blog</span></a></li><li class="wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content"  href="#"><span class="wp-block-navigation-item__label">About</span></a></li><li class="wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content"  href="#"><span class="wp-block-navigation-item__label">FAQs</span></a></li><li class="wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content"  href="#"><span class="wp-block-navigation-item__label">Authors</span></a></li></ul></nav>
1259
1260				<nav class="is-vertical wp-block-navigation is-layout-flex wp-container-core-navigation-is-layout-831b2db5 wp-block-navigation-is-layout-flex" aria-label=" 3"><ul class="wp-block-navigation__container is-vertical wp-block-navigation"><li class="wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content"  href="#"><span class="wp-block-navigation-item__label">Events</span></a></li><li class="wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content"  href="#"><span class="wp-block-navigation-item__label">Shop</span></a></li><li class="wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content"  href="#"><span class="wp-block-navigation-item__label">Patterns</span></a></li><li class="wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content"  href="#"><span class="wp-block-navigation-item__label">Themes</span></a></li></ul></nav>
1261			</div>
1262				
1263		</div>
1264		
1265
1266		
1267		<div style="height:var(--wp--preset--spacing--70)" aria-hidden="true" class="wp-block-spacer"></div>
1268		
1269
1270		
1271		<div class="wp-block-group alignfull is-content-justification-space-between is-layout-flex wp-container-core-group-is-layout-2ab8c7fb wp-block-group-is-layout-flex">
1272			
1273			<p class="has-small-font-size wp-block-paragraph">Twenty Twenty-Five</p>
1274			
1275			
1276			<p class="has-small-font-size wp-block-paragraph">
1277				Designed with <a href="https://wordpress.org" rel="nofollow">WordPress</a>			</p>
1278			
1279		</div>
1280		
1281	</div>
1282	
1283</div>
1284
1285
1286</footer>
1287</div>
1288<script type="speculationrules">
1289{"prefetch":[{"source":"document","where":{"and":[{"href_matches":"/blog/*"},{"not":{"href_matches":["/blog/wp-*.php","/blog/wp-admin/*","/blog/wp-content/uploads/*","/blog/wp-content/*","/blog/wp-content/plugins/*","/blog/wp-content/themes/twentytwentyfive/*","/blog/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]}
1290</script>
vendor: 1 bytes, line 1290
1290
1291<script data-wp-router-options="{&quot;loadOnClientNavigation&quot;:true}" fetchpriority="low" id="@wordpress/block-library/navigation/view-js-module" src="https://ko4bep.net/blog/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver=1bf28ded04f9f188bdcb" type="module"></script>
vendor: 1 bytes, line 1291
1291
1292<script id="wp-emoji-settings" type="application/json">
1293{"baseUrl":"https://s.w.org/images/core/emoji/17.0.2/72x72/","ext":".png","svgUrl":"https://s.w.org/images/core/emoji/17.0.2/svg/","svgExt":".svg","source":{"concatemoji":"https://ko4bep.net/blog/wp-includes/js/wp-emoji-release.min.js?ver=7.1"}}
1294</script>
vendor: 1 bytes, line 1294
1294
1295<script type="module">
1296/*! This file is auto-generated */
1297var e="script#wp-emoji-settings",t=document.querySelector(e);if(!(t instanceof HTMLScriptElement))throw new Error("Element missing: "+e);const r=JSON.parse(t.text),s=(window._wpemojiSettings=r,"wpEmojiSettingsSupports"),o=["flag","emoji"];function i(e){try{var t={supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(s,JSON.stringify(t))}catch(e){}}function c(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);t=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0);const r=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);return t.every((e,t)=>e===r[t])}function p(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var n=e.getImageData(16,16,1,1);for(let e=0;e<n.data.length;e++)if(0!==n.data[e])return!1;return!0}function u(e,t,n,r){switch(t){case"flag":return n(e,"\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f","\ud83c\udff3\ufe0f\u200b\u26a7\ufe0f")?!1:!n(e,"\ud83c\udde8\ud83c\uddf6","\ud83c\udde8\u200b\ud83c\uddf6")&&!n(e,"\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f","\ud83c\udff4\u200b\udb40\udc67\u200b\udb40\udc62\u200b\udb40\udc65\u200b\udb40\udc6e\u200b\udb40\udc67\u200b\udb40\udc7f");case"emoji":return!r(e,"\ud83e\u1fac8")}return!1}function f(e,t,n,r){let a;const s=(a="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?new OffscreenCanvas(300,150):document.createElement("canvas")).getContext("2d",{willReadFrequently:!0}),o=(s.textBaseline="top",s.font="600 32px Arial",{});return e.forEach(e=>{o[e]=t(s,e,n,r)}),o}function a(e){var t=document.createElement("script");t.src=e,t.defer=!0,document.head.appendChild(t)}r.supports={everything:!0,everythingExceptFlag:!0},new Promise(t=>{let n=function(){try{var e=JSON.parse(sessionStorage.getItem(s));if("object"==typeof e&&"number"==typeof e.timestamp&&(new Date).valueOf()<e.timestamp+604800&&"object"==typeof e.supportTests)return e.supportTests}catch(e){}return null}();if(!n){if("undefined"!=typeof Worker&&"undefined"!=typeof OffscreenCanvas&&"undefined"!=typeof URL&&URL.createObjectURL&&"undefined"!=typeof Blob)try{var e="postMessage("+f.toString()+"("+[JSON.stringify(o),u.toString(),c.toString(),p.toString()].join(",")+"));",r=new Blob([e],{type:"text/javascript"});const a=new Worker(URL.createObjectURL(r),{name:"wpTestEmojiSupports"});return void(a.onmessage=e=>{i(n=e.data),a.terminate(),t(n)})}catch(e){}i(n=f(o,u,c,p))}t(n)}).then(e=>{for(const n in e)r.supports[n]=e[n],r.supports.everything=r.supports.everything&&r.supports[n],"flag"!==n&&(r.supports.everythingExceptFlag=r.supports.everythingExceptFlag&&r.supports[n]);var t;r.supports.everythingExceptFlag=r.supports.everythingExceptFlag&&!r.supports.flag,r.supports.everything||((t=r.source||{}).concatemoji?a(t.concatemoji):t.wpemoji&&t.twemoji&&(a(t.twemoji),a(t.wpemoji)))});
1298//# sourceURL=https://ko4bep.net/blog/wp-includes/js/wp-emoji-loader.min.js
1299</script>
1299
1300</body>
1301</html>
1302
1303<!-- Dynamic page generated in 0.174 seconds. -->
1304<!-- Cached page generated by WP-Super-Cache on 2026-10-07 14:24:12 -->
1305
1306<!-- super cache -->

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.