Initial import of Hugo site to Forgejo

This commit is contained in:
Iman Alipour 2026-07-28 12:47:20 +00:00
commit cb1ba0317f
1259 changed files with 236349 additions and 0 deletions

8
public/404.html Normal file
View file

@ -0,0 +1,8 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>404 Page not found | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/404.html><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/404.html><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/404.html"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="404 Page not found"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="404 Page not found"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><div class=not-found>404</div></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

10
public/about/index.html Normal file

File diff suppressed because one or more lines are too long

1
public/about/index.xml Normal file
View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>About on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/</link><description>Recent content in About on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/index.xml" rel="self" type="application/rss+xml"/></channel></rss>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/</title>
<link rel="canonical" href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/">
<meta name="robots" content="noindex">
<meta charset="utf-8">
<meta http-equiv="refresh" content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/">
</head>
</html>

BIN
public/about/pfp.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
public/about/pfp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,14 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>DevOps | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/devops/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/devops/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/devops/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/devops/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="DevOps"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="DevOps"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/>Categories</a></div><h1>DevOps
<a href=/categories/devops/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><header class=entry-header><h2 class=entry-hint-parent>Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod</h2></header><div class=entry-content><p>TL;DR — The site is built once (Hugo project), published twice:
Onion: http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ via Tor, no TLS/HSTS, bound to 127.0.0.1:3301. Clearnet: https://blog.alipourimjourneys.ir behind Cloudflare, Lets Encrypt cert, Onion-Location header pointing to the onion mirror. 1) Tor hidden service (onion) basics I used Tors v3 onion services and mapped onion port 80 → my local web server on 127.0.0.1:3301.
Install & configure Tor (Debian/Ubuntu):
sudo apt update && sudo apt install -y tor sudoedit /etc/tor/torrc Add:
...</p></div><footer class=entry-footer><span title='2025-09-19 00:00:00 +0000 UTC'>September 19, 2025</span>&nbsp;·&nbsp;6 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,422 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>DevOps on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/devops/</link><description>Recent content in DevOps on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Fri, 19 Sep 2025 00:00:00 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/devops/index.xml" rel="self" type="application/rss+xml"/><item><title>Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/</link><pubDate>Fri, 19 Sep 2025 00:00:00 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/</guid><description>How I hosted a Hugo + PaperMod site both as a Tor onion service and a normal HTTPS site behind Cloudflare, with clean configs, dual builds, and a one-command deploy.</description><content:encoded><![CDATA[<blockquote>
<p>TL;DR — The site is built once (Hugo project), <strong>published twice</strong>:</p>
<ul>
<li><strong>Onion</strong>: <code>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/</code> via Tor, no TLS/HSTS, bound to <code>127.0.0.1:3301</code>.</li>
<li><strong>Clearnet</strong>: <code>https://blog.alipourimjourneys.ir</code> behind Cloudflare, Lets Encrypt cert, <code>Onion-Location</code> header pointing to the onion mirror.</li>
</ul></blockquote>
<hr>
<h2 id="1-tor-hidden-service-onion-basics">1) Tor hidden service (onion) basics</h2>
<p>I used Tors v3 onion services and mapped onion port 80 → my local web server on <code>127.0.0.1:3301</code>.</p>
<p><strong>Install &amp; configure Tor (Debian/Ubuntu):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt update <span style="color:#f92672">&amp;&amp;</span> sudo apt install -y tor
</span></span><span style="display:flex;"><span>sudoedit /etc/tor/torrc
</span></span></code></pre></div><p>Add:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 344 41"
>
<g transform='translate(8,16)'>
<path d='M 132,8 L 124,40' fill='none' stroke='currentColor'></path>
<path d='M 196,8 L 188,40' fill='none' stroke='currentColor'></path>
<path d='M 228,8 L 220,40' fill='none' stroke='currentColor'></path>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>8</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>7</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>/</text>
</g>
</svg>
</div>
<p>Make sure the directory is owned by Tors user and private:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo mkdir -p /var/lib/tor/hidden_site
</span></span><span style="display:flex;"><span>sudo chown -R debian-tor:debian-tor /var/lib/tor/hidden_site
</span></span><span style="display:flex;"><span>sudo chmod <span style="color:#ae81ff">700</span> /var/lib/tor/hidden_site
</span></span></code></pre></div><p><strong>Important:</strong> use the right systemd unit:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># validate as the Tor user</span>
</span></span><span style="display:flex;"><span>sudo -u debian-tor tor -f /etc/tor/torrc --verify-config
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># (re)start the real service</span>
</span></span><span style="display:flex;"><span>sudo systemctl enable --now tor@default
</span></span><span style="display:flex;"><span>sudo systemctl restart tor@default
</span></span></code></pre></div><p>Get the onion address:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo cat /var/lib/tor/hidden_site/hostname
</span></span></code></pre></div><p>Quick check (do remote DNS via SOCKS):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -I --socks5-hostname 127.0.0.1:9050 <span style="color:#e6db74">&#34;http://</span><span style="color:#66d9ef">$(</span>sudo cat /var/lib/tor/hidden_site/hostname<span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span>
</span></span></code></pre></div><hr>
<h2 id="2-nginx-for-the-onion-localhost-only">2) Nginx for the onion (localhost-only)</h2>
<p>I keep the onion site strictly on localhost: <strong>no HTTPS, no redirects, no HSTS</strong>. Tor already provides e2e encryption and authenticity.</p>
<p><code>/etc/nginx/sites-available/onion-blog</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> 127.0.0.1:<span style="color:#ae81ff">3301</span> <span style="color:#e6db74">default_server</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">&lt;your-56-char&gt;.onion</span> 127.0.0.1 <span style="color:#e6db74">localhost</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># keep onion simple; no HSTS/redirects here
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Referrer-Policy</span> <span style="color:#e6db74">no-referrer</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">X-Content-Type-Options</span> <span style="color:#e6db74">nosniff</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">X-Frame-Options</span> <span style="color:#e6db74">SAMEORIGIN</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># (optional) strict CSP so nothing leaks to clearnet
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e"># add_header Content-Security-Policy &#34;default-src &#39;self&#39;; img-src &#39;self&#39; data:; style-src &#39;self&#39; &#39;unsafe-inline&#39;; script-src &#39;self&#39;&#34; always;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/srv/hugo/mysite/public-onion</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">index</span> <span style="color:#e6db74">index.html</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> { <span style="color:#f92672">try_files</span> $uri $uri/ <span style="color:#e6db74">/index.html</span>; }
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> ~<span style="color:#e6db74">*</span> <span style="color:#e6db74">\.(css|js|ico|png|jpg|jpeg|gif|svg|webp|txt|xml)</span>$ {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">access_log</span> <span style="color:#66d9ef">off</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Cache-Control</span> <span style="color:#e6db74">&#34;public,</span> <span style="color:#e6db74">max-age=31536000,</span> <span style="color:#e6db74">immutable&#34;</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">try_files</span> $uri =<span style="color:#ae81ff">404</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Enable/reload:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo ln -sf /etc/nginx/sites-available/onion-blog /etc/nginx/sites-enabled/onion-blog
</span></span><span style="display:flex;"><span>sudo nginx -t <span style="color:#f92672">&amp;&amp;</span> sudo systemctl reload nginx
</span></span></code></pre></div><blockquote>
<p>Gotcha I hit: I had <strong>two</strong> server blocks on <code>127.0.0.1:3301</code>, which caused 404s via onion. Make sure only the intended vhost listens there (or mark it <code>default_server</code>). Also, if you ever use a regex in <code>server_name</code>, the syntax is <code>server_name ~* \.onion$</code> (note the space after <code>~*</code>).</p></blockquote>
<hr>
<h2 id="3-hugo--papermod-setup-and-version-bumps">3) Hugo + PaperMod setup (and version bumps)</h2>
<p>PaperMod now requires <strong>Hugo Extended ≥ 0.146.0</strong>. I installed the extended binary from the official tarball to avoid Snaps sandbox limitations (Snap cant read <code>/srv</code> paths by default).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># install Hugo extended (example)</span>
</span></span><span style="display:flex;"><span>VER<span style="color:#f92672">=</span>0.146.0
</span></span><span style="display:flex;"><span>cd /tmp
</span></span><span style="display:flex;"><span>wget https://github.com/gohugoio/hugo/releases/download/v<span style="color:#e6db74">${</span>VER<span style="color:#e6db74">}</span>/hugo_extended_<span style="color:#e6db74">${</span>VER<span style="color:#e6db74">}</span>_Linux-amd64.tar.gz
</span></span><span style="display:flex;"><span>tar -xzf hugo_extended_<span style="color:#e6db74">${</span>VER<span style="color:#e6db74">}</span>_Linux-amd64.tar.gz
</span></span><span style="display:flex;"><span>sudo mv hugo /usr/local/bin/hugo
</span></span><span style="display:flex;"><span>hugo version <span style="color:#75715e"># should say &#34;extended&#34; and &gt;= 0.146.0</span>
</span></span></code></pre></div><p>Create the site and theme:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo mkdir -p /srv/hugo <span style="color:#f92672">&amp;&amp;</span> sudo chown -R <span style="color:#e6db74">&#34;</span>$USER<span style="color:#e6db74">&#34;</span>:<span style="color:#e6db74">&#34;</span>$USER<span style="color:#e6db74">&#34;</span> /srv/hugo
</span></span><span style="display:flex;"><span>cd /srv/hugo
</span></span><span style="display:flex;"><span>hugo new site mysite
</span></span><span style="display:flex;"><span>cd mysite
</span></span><span style="display:flex;"><span>git init
</span></span><span style="display:flex;"><span>git submodule add https://github.com/adityatelange/hugo-PaperMod themes/PaperMod
</span></span></code></pre></div><p><strong>Config updates:</strong> in newer Hugo, <code>paginate</code> is deprecated → use:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span><span style="color:#75715e"># config/_default/hugo.toml</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">title</span> = <span style="color:#e6db74">&#34;My Blog&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">theme</span> = <span style="color:#e6db74">&#34;PaperMod&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">enableRobotsTXT</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">pagination</span>]
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">pagerSize</span> = <span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">params</span>]
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">defaultTheme</span> = <span style="color:#e6db74">&#34;auto&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showReadingTime</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showPostNavLinks</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showBreadCrumbs</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showCodeCopyButtons</span> = <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><p>I added per-environment overrides so I can build two outputs with different <code>baseURL</code>s:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span><span style="color:#75715e"># config/clearnet/hugo.toml</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">baseURL</span> = <span style="color:#e6db74">&#34;https://blog.alipourimjourneys.ir/&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># config/onion/hugo.toml</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">baseURL</span> = <span style="color:#e6db74">&#34;http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/&#34;</span>
</span></span></code></pre></div><hr>
<h2 id="4-dual-builds-clearnet--onion-and-one-command-deploy">4) Dual builds (clearnet + onion) and one-command deploy</h2>
<p>I publish the same content twice—once for each base URL and docroot:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd /srv/hugo/mysite
</span></span><span style="display:flex;"><span><span style="color:#75715e"># clearnet build (served over HTTPS)</span>
</span></span><span style="display:flex;"><span>hugo --minify --environment clearnet -d public-clearnet
</span></span><span style="display:flex;"><span><span style="color:#75715e"># onion build (served via Tor)</span>
</span></span><span style="display:flex;"><span>hugo --minify --environment onion -d public-onion
</span></span><span style="display:flex;"><span>sudo systemctl reload nginx
</span></span></code></pre></div><p>Helper script I use:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo tee /usr/local/bin/build-both &gt;/dev/null <span style="color:#e6db74">&lt;&lt;&#39;EOF&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">set -euo pipefail
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">cd /srv/hugo/mysite
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">hugo --minify --environment clearnet -d public-clearnet
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">hugo --minify --environment onion -d public-onion
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">sudo systemctl reload nginx
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">echo &#34;Deployed both at $(date)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span>sudo chmod +x /usr/local/bin/build-both
</span></span></code></pre></div><p>While editing, I sometimes auto-rebuild on file save:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt install -y entr
</span></span><span style="display:flex;"><span>cd /srv/hugo/mysite
</span></span><span style="display:flex;"><span>find content layouts assets static config -type f | entr -r build-both
</span></span></code></pre></div><hr>
<h2 id="5-clearnet-behind-cloudflare--lets-encrypt-manual-dns-01">5) Clearnet behind Cloudflare + Lets Encrypt (manual DNS-01)</h2>
<p>Cloudflare is set to <strong>Full (strict)</strong>. I issued a public cert for <code>blog.alipourimjourneys.ir</code> using <strong>manual DNS-01</strong> (no API token):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo snap install --classic certbot
</span></span><span style="display:flex;"><span>sudo certbot certonly --manual --preferred-challenges dns -d blog.alipourimjourneys.ir --agree-tos -m you@example.com --no-eff-email
</span></span></code></pre></div><p>Certbot tells you to add a TXT record <code>_acme-challenge.blog.alipourimjourneys.ir</code>. Add it in Cloudflare DNS, verify with <code>dig</code>, then continue. Cert ends up at:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 496 41"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='280' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='288' y='20' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='296' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='304' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='312' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='320' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='336' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='344' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='352' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='360' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='368' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='376' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='384' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='392' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='400' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='408' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='416' y='20' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='424' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='432' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='440' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='448' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='456' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='464' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>m</text>
</g>
</svg>
</div>
<p>Clearnet Nginx vhosts:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#75715e"># HTTP → HTTPS redirect (optional; CF usually talks HTTPS to origin anyway)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">80</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:80</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">blog.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">return</span> <span style="color:#ae81ff">301</span> <span style="color:#e6db74">https://blog.alipourimjourneys.ir</span>$request_uri;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># HTTPS origin (behind Cloudflare)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">blog.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/blog.alipourimjourneys.ir/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/blog.alipourimjourneys.ir/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># HSTS on clearnet only
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Strict-Transport-Security</span> <span style="color:#e6db74">&#34;max-age=31536000</span>; <span style="color:#f92672">includeSubDomains</span>; <span style="color:#f92672">preload&#34;</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Help Tor Browser discover the onion mirror (safe on clearnet)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Onion-Location</span> <span style="color:#e6db74">&#34;http://&lt;your-56-char&gt;.onion</span>$request_uri&#34; <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/srv/hugo/mysite/public-clearnet</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">index</span> <span style="color:#e6db74">index.html</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> { <span style="color:#f92672">try_files</span> $uri $uri/ <span style="color:#e6db74">/index.html</span>; }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>Note: dont add HSTS or HTTPS redirects to the <strong>onion</strong> vhost. Keep onion pure HTTP on localhost.</p></blockquote>
<hr>
<h2 id="6-troubleshooting-i-ran-into-and-fixes">6) Troubleshooting I ran into (and fixes)</h2>
<ul>
<li><strong>Tor unit confusion:</strong> <code>tor.service</code> is a tiny master; the real daemon is <code>tor@default</code>. Use that unit and verify config as <code>debian-tor</code>.</li>
<li><strong>Permissions:</strong> <code>HiddenServiceDir</code> must be owned by <code>debian-tor</code> and mode <code>700</code>.</li>
<li><strong>Mapping mismatch:</strong> If <code>HiddenServicePort 80 127.0.0.1:3301</code> is set, visit <code>http://&lt;onion&gt;/</code> (no <code>:3301</code>). If you set <code>HiddenServicePort 3301 127.0.0.1:3301</code>, you must use <code>http://&lt;onion&gt;:3301/</code>.</li>
<li><strong>Curl &amp; .onion:</strong> modern curl refuses <code>.onion</code> unless you use remote DNS via SOCKS:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -I --socks5-hostname 127.0.0.1:9050 <span style="color:#e6db74">&#34;http://&lt;onion&gt;/&#34;</span>
</span></span></code></pre></div></li>
<li><strong>Two Nginx vhosts on the same port:</strong> I had a duplicate server on <code>127.0.0.1:3301</code> pointing somewhere else, which caused onion 404s. Keep only one (or mark one <code>default_server</code>).</li>
<li><strong>Regex in <code>server_name</code>:</strong> if you use it, write <code>server_name ~* \.onion$</code> (space after <code>~*</code>). I fixed an <code>invalid variable name</code> error caused by a missing space.</li>
<li><strong>PaperMod with old Hugo:</strong> upgraded to <strong>extended ≥ 0.146.0</strong>. Also updated <code>paginate</code><code>[pagination].pagerSize</code>.</li>
<li><strong>Snap confinement:</strong> Snaps <code>hugo</code> couldnt read <code>/srv</code> (<code>.../void: permission denied</code>). Switched to the tarball build in <code>/usr/local/bin</code>.</li>
</ul>
<hr>
<h2 id="7-not-secure-in-tor-browser">7) “Not secure” in Tor Browser?</h2>
<p>That message can appear because onion uses <strong>HTTP</strong>. Its OK: Tor provides e2e encryption + onion auth. If you enable HTTPS-Only Mode, add an exception for the site. Also ensure the onion build doesnt reference <strong>clearnet</strong> resources (scan the built HTML for <code>http(s)://</code> links that arent your onion).</p>
<hr>
<h2 id="8-day-to-day-workflow">8) Day-to-day workflow</h2>
<ul>
<li>Create content:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>hugo new posts/my-first-post.md <span style="color:#75715e"># then set draft: false</span>
</span></span></code></pre></div></li>
<li>Publish both:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>build-both
</span></span></code></pre></div></li>
<li>(Optional) Auto on save:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>find content layouts assets static config -type f | entr -r build-both
</span></span></code></pre></div></li>
<li>(Optional) Git push-to-deploy with a bare repo + post-receive hook that runs <code>build-both</code>.</li>
</ul>
<hr>
<h2 id="final-notes">Final notes</h2>
<ul>
<li>Clearnet gets <strong>HTTPS + HSTS</strong> and an <code>Onion-Location</code> header.</li>
<li>Onion gets <strong>no HSTS/redirects</strong>, and all assets are self-hosted to avoid mixed content.</li>
<li>Serving both worlds from one Hugo repo is easy: <strong>two builds, two vhosts, one workflow</strong>.</li>
</ul>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/devops/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/devops/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/devops/"></head></html>

View file

@ -0,0 +1,10 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Games | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/games/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/games/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/games/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/games/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Games"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Games"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/>Categories</a></div><h1>Games
<a href=/categories/games/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><header class=entry-header><h2 class=entry-hint-parent>Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases</h2></header><div class=entry-content><p>How I containerized a Java Minecraft server with Geyser, hit a /var space wall, and locked the server to the latest stable release.</p></div><footer class=entry-footer><span title='2025-09-29 09:06:29 +0000 UTC'>September 29, 2025</span>&nbsp;·&nbsp;3 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,259 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Games on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/games/</link><description>Recent content in Games on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Mon, 29 Sep 2025 09:06:29 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/games/index.xml" rel="self" type="application/rss+xml"/><item><title>Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/</link><pubDate>Mon, 29 Sep 2025 09:06:29 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/</guid><description>How I containerized a Java Minecraft server with Geyser, hit a /var space wall, and locked the server to the latest stable release.</description><content:encoded><![CDATA[<h2 id="why">Why</h2>
<p>Ive been running a Java Minecraft world (with Bedrock players via <strong>Geyser</strong>) in <code>tmux</code>. I moved it to Docker for easier updates, backups, and restarts. Along the way I hit:</p>
<ul>
<li><code>failed to register layer: ... no space left on device</code></li>
<li>Client saying: <strong>“Outdated client, please use 1.21.9 Pre-Release 2”</strong></li>
<li>Wanting config in a neat <code>.env</code> and <strong>no whitelist</strong></li>
</ul>
<p>Heres exactly what I did.</p>
<hr>
<h2 id="folder-layout">Folder layout</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>~/minecraft/
</span></span><span style="display:flex;"><span>├─ docker-compose.yml
</span></span><span style="display:flex;"><span>├─ .env
</span></span><span style="display:flex;"><span>└─ plugins/
</span></span></code></pre></div><hr>
<h2 id="env-secrets--knobs"><code>.env</code> (secrets &amp; knobs)</h2>
<p>Use the <strong>latest stable</strong> (not snapshots/pre-releases) by setting <code>VERSION=LATEST</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-env" data-lang="env"><span style="display:flex;"><span><span style="color:#75715e"># Minecraft server configuration</span>
</span></span><span style="display:flex;"><span>EULA<span style="color:#f92672">=</span>TRUE
</span></span><span style="display:flex;"><span>TYPE<span style="color:#f92672">=</span>PAPER
</span></span><span style="display:flex;"><span>VERSION<span style="color:#f92672">=</span>LATEST
</span></span><span style="display:flex;"><span>MEMORY<span style="color:#f92672">=</span>4G
</span></span><span style="display:flex;"><span>USE_AIKAR_FLAGS<span style="color:#f92672">=</span>true
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># RCON (remote console)</span>
</span></span><span style="display:flex;"><span>ENABLE_RCON<span style="color:#f92672">=</span>true
</span></span><span style="display:flex;"><span>RCON_PASSWORD<span style="color:#f92672">=</span>superSecretPassword123
</span></span></code></pre></div><blockquote>
<p>I dont use a whitelist, so no <code>WHITELIST</code>/<code>ENFORCE_WHITELIST</code> variables.</p></blockquote>
<hr>
<h2 id="docker-composeyml"><code>docker-compose.yml</code></h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">mc</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">itzg/minecraft-server:latest</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">mc</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;25565:25565&#34;</span> <span style="color:#75715e"># Java</span>
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;19132:19132/udp&#34;</span> <span style="color:#75715e"># Bedrock via Geyser plugin</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">env_file</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">.env</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">mc-data:/data</span>
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">./plugins:/plugins:ro</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">mc-data</span>: {}
</span></span></code></pre></div><blockquote>
<p>The <code>version:</code> key in Compose is obsolete now, so I dropped it.</p></blockquote>
<hr>
<h2 id="add-geyser-and-optional-floodgate-as-plugins">Add Geyser (and optional Floodgate) as plugins</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir -p ~/minecraft/plugins
</span></span><span style="display:flex;"><span>cd ~/minecraft/plugins
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Geyser (Spigot/Paper)</span>
</span></span><span style="display:flex;"><span>wget https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot -O Geyser-Spigot.jar
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Floodgate (optional: Bedrock accounts without Java linking)</span>
</span></span><span style="display:flex;"><span>wget https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot -O Floodgate-Spigot.jar
</span></span></code></pre></div><p>Start it up:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose up -d
</span></span></code></pre></div><p>On first run, Geyser writes its config to <code>/data/plugins/Geyser-Spigot/</code>. Open UDP <strong>19132</strong> on your firewall.</p>
<hr>
<h2 id="hit-a-wall-var-ran-out-of-space">Hit a wall: <code>/var</code> ran out of space</h2>
<p>Docker stores layers at <code>/var/lib/docker</code> by default. My <code>/var</code> LV was tiny:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 448 25"
>
<g transform='translate(8,16)'>
<path d='M 404,8 L 404,24' fill='none' stroke='currentColor'></path>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>%</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>r</text>
</g>
</svg>
</div>
<h3 id="quick-cleanup">Quick cleanup</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker system df
</span></span><span style="display:flex;"><span>docker system prune -f
</span></span><span style="display:flex;"><span>docker builder prune -af
</span></span><span style="display:flex;"><span>sudo apt-get clean
</span></span><span style="display:flex;"><span>sudo journalctl --vacuum-size<span style="color:#f92672">=</span>100M
</span></span></code></pre></div><p>If thats not enough, you have two good options:</p>
<h3 id="option-a--move-dockers-data-off-var">Option A — Move Dockers data off <code>/var</code></h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo systemctl stop docker
</span></span><span style="display:flex;"><span>sudo mkdir -p /home/docker
</span></span><span style="display:flex;"><span>sudo rsync -aHAX --info<span style="color:#f92672">=</span>progress2 /var/lib/docker/ /home/docker/
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#39;{ &#34;data-root&#34;: &#34;/home/docker&#34; }&#39;</span> | sudo tee /etc/docker/daemon.json
</span></span><span style="display:flex;"><span>sudo systemctl start docker
</span></span><span style="display:flex;"><span>docker info | grep <span style="color:#e6db74">&#34;Docker Root Dir&#34;</span>
</span></span></code></pre></div><p>If all looks good, free the old space:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo rm -rf /var/lib/docker/*
</span></span></code></pre></div><h3 id="option-b--grow-var-lvm">Option B — Grow <code>/var</code> (LVM)</h3>
<p>Check free space in the VG:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo vgdisplay <span style="color:#75715e"># look for &#34;Free PE / Size&#34;</span>
</span></span></code></pre></div><p>If available, extend <code>/var</code> by +5G:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo lvextend -L +5G /dev/mapper/ubuntu--vg-var
</span></span><span style="display:flex;"><span>sudo resize2fs /dev/mapper/ubuntu--vg-var
</span></span></code></pre></div><hr>
<h2 id="the-version-mismatch-pre-release-vs-stable">The version mismatch: pre-release vs stable</h2>
<p>My logs showed:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 440 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>2</text>
</g>
</svg>
</div>
<p>That happens if the server pulls a pre-release build (or if <code>VERSION=LATEST</code>). Fix:</p>
<ol>
<li>Ensure <code>.env</code> has:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-env" data-lang="env"><span style="display:flex;"><span>VERSION<span style="color:#f92672">=</span>LATEST_RELEASE
</span></span></code></pre></div></li>
<li>Recreate:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose down
</span></span><span style="display:flex;"><span>docker compose pull
</span></span><span style="display:flex;"><span>docker compose up -d
</span></span></code></pre></div></li>
<li>Verify:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker logs mc | grep <span style="color:#e6db74">&#34;Starting minecraft server version&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># -&gt; Starting minecraft server version 1.21.1 (example)</span>
</span></span></code></pre></div></li>
</ol>
<blockquote>
<p>Tip: If you <strong>want to pin</strong> and avoid auto-updates entirely, set <code>VERSION=1.21.1</code> (or whatever stable youve validated).</p></blockquote>
<hr>
<h2 id="no-whitelist">No whitelist</h2>
<p>Because I dont set <code>WHITELIST</code>/<code>ENFORCE_WHITELIST</code>, anyone can join (subject to online-mode, bans, and Geyser/Floodgate auth settings). Manage ops/permissions via:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker exec -it mc rcon-cli <span style="color:#e6db74">&#34;op YourJavaIGN&#34;</span>
</span></span></code></pre></div><p>(or edit <code>/data/ops.json</code>).</p>
<hr>
<h2 id="backup--updates">Backup &amp; updates</h2>
<ul>
<li>World/data live under the <code>mc-data</code> volume → back up <code>/data</code> regularly.</li>
<li>Update cleanly:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose pull <span style="color:#f92672">&amp;&amp;</span> docker compose up -d
</span></span></code></pre></div></li>
<li>Watch space:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>watch -n5 <span style="color:#e6db74">&#39;df -h /var; docker system df&#39;</span>
</span></span></code></pre></div></li>
</ul>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Put <strong>Geyser/Floodgate</strong> jars in <code>./plugins</code> and expose <strong>19132/udp</strong>.</li>
<li>Keep configs in <code>.env</code>.</li>
<li>Fix <code>/var</code> space by pruning, <strong>moving Dockers data-root</strong>, or <strong>extending <code>/var</code></strong> via LVM.</li>
<li>Verify version in logs after each change.</li>
</ul>
<p>Happy block-breaking! 🧱🚀</p>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/games/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/games/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/games/"></head></html>

View file

@ -0,0 +1,17 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Guides | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/guides/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/guides/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/guides/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/guides/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Guides"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Guides"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/>Categories</a></div><h1>Guides
<a href=/categories/guides/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><header class=entry-header><h2 class=entry-hint-parent>Stealth Trojan VPN Behind Cloudflare Guide</h2></header><div class=entry-content><p>Introduction So you want a VPN that doesnt scream “I am a VPN” to every censor and firewall out there?
Welcome to the world of Trojan over WebSocket + TLS behind Cloudflare.
This guide not only shows you how to set it up but also sprinkles in some debugging magic so you can figure out why things break (and they will break, trust me).
Well anonymise domains and secrets, so substitute with your own:
...</p></div><footer class=entry-footer><span title='2025-09-09 11:05:00 +0200 +0200'>September 9, 2025</span>&nbsp;·&nbsp;4 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Stealth Trojan VPN Behind Cloudflare Guide" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/></a></article><article class="post-entry tag-entry"><figure class=entry-cover><img loading=lazy src=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/images/hedgedoc-cover.png alt="HedgeDoc collaborative editing"></figure><header class=entry-header><h2 class=entry-hint-parent>Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt</h2></header><div class=entry-content><p>HedgeDoc is an open-source collaborative Markdown editor. Think Google Docs for Markdown: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post well walk through setting up your own instance on a server, secured with HTTPS.
Prerequisites A Linux server with Docker and Docker Compose A domain name pointing to your server (e.g. notes.alipourimjourneys.ir) Nginx installed for reverse proxying Certbot for Lets Encrypt certificates 1. Create the project directory mkdir ~/hedgedoc && cd ~/hedgedoc 2. Create .env POSTGRES_PASSWORD=ChangeThisStrongPassword HD_DOMAIN=notes.alipourimjourneys.ir Generate a strong password with:
...</p></div><footer class=entry-footer><span title='2025-08-29 00:00:00 +0000 UTC'>August 29, 2025</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,508 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Guides on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/guides/</link><description>Recent content in Guides on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Tue, 09 Sep 2025 11:05:00 +0200</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/guides/index.xml" rel="self" type="application/rss+xml"/><item><title>Stealth Trojan VPN Behind Cloudflare Guide</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/</link><pubDate>Tue, 09 Sep 2025 11:05:00 +0200</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>So you want a VPN that &lt;strong>doesn&amp;rsquo;t scream &amp;ldquo;I am a VPN&amp;rdquo;&lt;/strong> to every censor and firewall out there?&lt;br>
Welcome to the world of &lt;strong>Trojan over WebSocket + TLS behind Cloudflare&lt;/strong>.&lt;/p>
&lt;p>This guide not only shows you how to set it up but also sprinkles in some &lt;strong>debugging magic&lt;/strong> so you can figure out why things break (and they &lt;em>will&lt;/em> break, trust me).&lt;/p>
&lt;p>Well anonymise domains and secrets, so substitute with your own:&lt;/p></description><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>So you want a VPN that <strong>doesn&rsquo;t scream &ldquo;I am a VPN&rdquo;</strong> to every censor and firewall out there?<br>
Welcome to the world of <strong>Trojan over WebSocket + TLS behind Cloudflare</strong>.</p>
<p>This guide not only shows you how to set it up but also sprinkles in some <strong>debugging magic</strong> so you can figure out why things break (and they <em>will</em> break, trust me).</p>
<p>Well anonymise domains and secrets, so substitute with your own:</p>
<ul>
<li>VPN domain: <code>web.example.com</code></li>
<li>Panel domain: <code>panel.example.com</code></li>
<li>Secret WS path: <code>/stealth-path_abcd1234</code></li>
<li>Password: <code>&lt;PASSWORD&gt;</code></li>
</ul>
<hr>
<h2 id="architecture-at-a-glance">Architecture at a Glance</h2>
<p>Think of it as a disguise party:</p>
<ul>
<li><strong>Trojan</strong> = the shy guest (your VPN protocol)</li>
<li><strong>Nginx</strong> = the bouncer checking IDs (reverse proxy)</li>
<li><strong>Cloudflare</strong> = the doorman who makes sure nobody sees who&rsquo;s inside (CDN &amp; proxy)</li>
<li><strong>Your fake website</strong> = the mask (camouflage page)</li>
</ul>
<p>Traffic flow:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 536 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'></text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'></text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>N</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'></text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>5</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='504' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='512' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='520' y='4' fill='currentColor' style='font-size:1em'>)</text>
</g>
</svg>
</div>
<hr>
<h2 id="step-1-panel-setup-panelexamplecom">Step 1: Panel Setup (<code>panel.example.com</code>)</h2>
<ul>
<li>Bind the 3x-ui panel to localhost (e.g., <code>127.0.0.1:46309</code>).</li>
<li>Choose a funky <strong>web base path</strong> like <code>/panel-bananas_42/</code>.</li>
<li>Proxy it through Nginx with HTTPS + Basic Auth.</li>
<li>Test it at <code>https://panel.example.com/panel-bananas_42/</code>.</li>
</ul>
<p><strong>Pro tip:</strong> If you see a blank page → your base path is mismatched or Nginx is eating it. Check logs!</p>
<hr>
<h2 id="step-2-trojan-inbound">Step 2: Trojan Inbound</h2>
<p>In 3x-ui, create a Trojan inbound:</p>
<ul>
<li>Local port: <code>54321</code></li>
<li>Transport: WebSocket</li>
<li>Path: <code>/stealth-path_abcd1234</code></li>
<li>Security: none</li>
<li>Password: <code>&lt;PASSWORD&gt;</code></li>
</ul>
<hr>
<h2 id="step-3-nginx-for-vpn-domain-webexamplecom">Step 3: Nginx for VPN Domain (<code>web.example.com</code>)</h2>
<p>Your Nginx is the gatekeeper. Sample config:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">web.example.com</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/web.example.com/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/web.example.com/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Fake website at root
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/var/www/html</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">index</span> <span style="color:#e6db74">index.html</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Real VPN under secret WS path
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/stealth-path_abcd1234</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:54321</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_http_version</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">.1</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">&#34;upgrade&#34;</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><hr>
<h2 id="step-4-firewall-rules">Step 4: Firewall Rules</h2>
<p>Lock things down! Only Cloudflare should reach you:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 22/tcp
</span></span><span style="display:flex;"><span>ufw allow 80/tcp
</span></span><span style="display:flex;"><span>ufw allow 443/tcp
</span></span><span style="display:flex;"><span>ufw deny 54321/tcp
</span></span></code></pre></div><hr>
<h2 id="step-5-client-config">Step 5: Client Config</h2>
<p>Trojan URI:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 1160 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>&lt;</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>W</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>&gt;</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>@</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>?</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='504' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='512' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='520' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='528' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='536' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='544' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='552' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='560' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='568' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='576' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='584' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='592' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='600' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='608' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='616' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='624' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='632' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='640' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='648' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='656' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='664' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='672' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='680' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='688' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='696' y='4' fill='currentColor' style='font-size:1em'>%</text>
<text text-anchor='middle' x='704' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='712' y='4' fill='currentColor' style='font-size:1em'>F</text>
<text text-anchor='middle' x='720' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='728' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='736' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='744' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='752' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='760' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='768' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='776' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='784' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='792' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='800' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='808' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='816' y='4' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='824' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='832' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='840' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='848' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='856' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='864' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='872' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='880' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='888' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='896' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='904' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='912' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='920' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='928' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='936' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='944' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='952' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='960' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='968' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='976' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='984' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='992' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='1000' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='1008' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='1016' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='1024' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='1032' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='1040' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='1048' y='4' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='1056' y='4' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='1064' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='1072' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='1080' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='1088' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='1096' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='1104' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='1112' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='1120' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='1128' y='4' fill='currentColor' style='font-size:1em'>V</text>
<text text-anchor='middle' x='1136' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='1144' y='4' fill='currentColor' style='font-size:1em'>N</text>
</g>
</svg>
</div>
<p>iOS clients: Shadowrocket, Stash, FoXray<br>
Android clients: v2rayNG, Clash Meta, NekoBox</p>
<hr>
<h2 id="debugging-time-aka-why-the-heck-doesnt-it-work">Debugging Time (a.k.a. “Why the heck doesnt it work?!”)</h2>
<h3 id="symptom-520-unknown-error-cloudflare">Symptom: <strong>520 Unknown Error (Cloudflare)</strong></h3>
<ul>
<li>Likely cause: Nginx couldnt talk to Trojan.</li>
<li>Check Nginx error log:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>tail -n <span style="color:#ae81ff">50</span> /var/log/nginx/error.log
</span></span></code></pre></div></li>
<li>If you see <code>upstream sent no valid HTTP/1.0 header</code> → youre mixing HTTPS/HTTP between Nginx and Trojan.</li>
</ul>
<hr>
<h3 id="symptom-526-invalid-ssl-certificate">Symptom: <strong>526 Invalid SSL certificate</strong></h3>
<ul>
<li>Cloudflare → Nginx cert mismatch.</li>
<li>Run:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>openssl s_client -connect web.example.com:443 -servername web.example.com -showcerts
</span></span></code></pre></div></li>
<li>Make sure CN = <code>web.example.com</code>. If not, fix your cert.</li>
</ul>
<hr>
<h3 id="symptom-blank-page-on-panel">Symptom: <strong>Blank page on panel</strong></h3>
<ul>
<li>Check if the base path matches exactly (case-sensitive, slash-sensitive).</li>
<li>Watch for sneaky trailing spaces!</li>
<li>Test locally:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -s -D- http://127.0.0.1:46309/panel-bananas_42/
</span></span></code></pre></div></li>
</ul>
<hr>
<h3 id="symptom-client-wont-connect">Symptom: <strong>Client wont connect</strong></h3>
<ul>
<li>Run a WebSocket test:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -i -k -H <span style="color:#e6db74">&#34;Connection: Upgrade&#34;</span> -H <span style="color:#e6db74">&#34;Upgrade: websocket&#34;</span> https://web.example.com/stealth-path_abcd1234
</span></span></code></pre></div>Expect <code>101 Switching Protocols</code>. If not, check Nginx config.</li>
</ul>
<hr>
<h3 id="symptom-censor-still-blocks-you">Symptom: <strong>Censor still blocks you</strong></h3>
<ul>
<li>Did you expose another service (like SSH, Matrix, or Minecraft) on the same IP?<br>
→ Theyll find your origin IP. Use a second VPS or lock those ports down.</li>
<li>Did you use an obvious path like <code>/ws</code>?<br>
→ Use a random-looking one like <code>/cdn-assets-329df/</code>.</li>
</ul>
<hr>
<h2 id="pro-tips--fun-tricks">Pro Tips &amp; Fun Tricks</h2>
<ul>
<li>Serve a fake blog or portfolio at root so your domain looks legit.</li>
<li>Rotate WebSocket paths occasionally.</li>
<li>Use Cloudflare <strong>Page Rules</strong> to disable Rocket Loader &amp; Minify for your VPN domain.</li>
<li>Make friends with your Nginx error.log — it will roast you but it tells the truth.</li>
</ul>
<hr>
<h2 id="conclusion">Conclusion</h2>
<p>By putting Trojan behind Cloudflare, youve given your VPN a shiny new disguise:</p>
<ul>
<li>Looks like normal HTTPS.</li>
<li>Hides your origin IP.</li>
<li>Forces censors into a tough choice: block Cloudflare (and half the web) or let you pass.</li>
</ul>
<p>Congrats — youve built a VPN with both <strong>style</strong> and <strong>stealth</strong>. 🥷</p>
]]></content:encoded></item><item><title>Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/</link><pubDate>Fri, 29 Aug 2025 00:00:00 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/</guid><description>&lt;p>HedgeDoc is an open-source collaborative Markdown editor. Think &lt;em>Google Docs for Markdown&lt;/em>: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post well walk through setting up your own instance on a server, secured with HTTPS.&lt;/p>
&lt;hr>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;ul>
&lt;li>A Linux server with &lt;strong>Docker&lt;/strong> and &lt;strong>Docker Compose&lt;/strong>&lt;/li>
&lt;li>A domain name pointing to your server (e.g. &lt;code>notes.alipourimjourneys.ir&lt;/code>)&lt;/li>
&lt;li>&lt;strong>Nginx&lt;/strong> installed for reverse proxying&lt;/li>
&lt;li>&lt;strong>Certbot&lt;/strong> for Lets Encrypt certificates&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="1-create-the-project-directory">1. Create the project directory&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>mkdir ~/hedgedoc &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> cd ~/hedgedoc&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;hr>
&lt;h2 id="2-create-env">2. Create &lt;code>.env&lt;/code>&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-env" data-lang="env">&lt;span style="display:flex;">&lt;span>POSTGRES_PASSWORD&lt;span style="color:#f92672">=&lt;/span>ChangeThisStrongPassword
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>HD_DOMAIN&lt;span style="color:#f92672">=&lt;/span>notes.alipourimjourneys.ir&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Generate a strong password with:&lt;/p></description><content:encoded><![CDATA[<p>HedgeDoc is an open-source collaborative Markdown editor. Think <em>Google Docs for Markdown</em>: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post well walk through setting up your own instance on a server, secured with HTTPS.</p>
<hr>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>A Linux server with <strong>Docker</strong> and <strong>Docker Compose</strong></li>
<li>A domain name pointing to your server (e.g. <code>notes.alipourimjourneys.ir</code>)</li>
<li><strong>Nginx</strong> installed for reverse proxying</li>
<li><strong>Certbot</strong> for Lets Encrypt certificates</li>
</ul>
<hr>
<h2 id="1-create-the-project-directory">1. Create the project directory</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir ~/hedgedoc <span style="color:#f92672">&amp;&amp;</span> cd ~/hedgedoc</span></span></code></pre></div>
<hr>
<h2 id="2-create-env">2. Create <code>.env</code></h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-env" data-lang="env"><span style="display:flex;"><span>POSTGRES_PASSWORD<span style="color:#f92672">=</span>ChangeThisStrongPassword
</span></span><span style="display:flex;"><span>HD_DOMAIN<span style="color:#f92672">=</span>notes.alipourimjourneys.ir</span></span></code></pre></div>
<p>Generate a strong password with:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>openssl rand -base64 <span style="color:#ae81ff">32</span></span></span></code></pre></div>
<hr>
<h2 id="3-create-docker-composeyml">3. Create <code>docker-compose.yml</code></h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">version</span>: <span style="color:#e6db74">&#34;3.9&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">db</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">postgres:16</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_USER</span>: <span style="color:#ae81ff">hedgedoc</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_PASSWORD</span>: <span style="color:#ae81ff">${POSTGRES_PASSWORD}</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_DB</span>: <span style="color:#ae81ff">hedgedoc</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">db:/var/lib/postgresql/data</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">hedgedoc</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">quay.io/hedgedoc/hedgedoc:1.10.2</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">depends_on</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">db</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_DB_URL</span>: <span style="color:#ae81ff">postgres://hedgedoc:${POSTGRES_PASSWORD}@db:5432/hedgedoc</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_DOMAIN</span>: <span style="color:#ae81ff">${HD_DOMAIN}</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_PROTOCOL_USESSL</span>: <span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_URL_ADDPORT</span>: <span style="color:#e6db74">&#34;false&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_PORT</span>: <span style="color:#e6db74">&#34;3000&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_EMAIL</span>: <span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_ALLOW_EMAIL_REGISTER</span>: <span style="color:#e6db74">&#34;false&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">uploads:/hedgedoc/public/uploads</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;127.0.0.1:3000:3000&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">db</span>:
</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">uploads:</span></span></span></code></pre></div>
<p>Bring it up:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose up -d</span></span></code></pre></div>
<hr>
<h2 id="4-get-a-lets-encrypt-certificate">4. Get a Lets Encrypt certificate</h2>
<p>Request a cert with a <strong>DNS challenge</strong>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo certbot certonly --manual --preferred-challenges dns -d notes.alipourimjourneys.ir -m you@example.com --agree-tos --no-eff-email</span></span></code></pre></div>
<p>Add the TXT record certbot asks for, wait for DNS to propagate, then continue.<br>
Certificates will be in:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>/etc/letsencrypt/live/notes.alipourimjourneys.ir/</span></span></code></pre></div>
<hr>
<h2 id="5-configure-nginx">5. Configure Nginx</h2>
<p>Create <code>/etc/nginx/sites-available/notes.alipourimjourneys.ir</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">notes.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">80</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:80</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">return</span> <span style="color:#ae81ff">301</span> <span style="color:#e6db74">https://</span>$host$request_uri;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">notes.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/notes.alipourimjourneys.ir/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/notes.alipourimjourneys.ir/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:3000</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Real-IP</span> $remote_addr;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $proxy_add_x_forwarded_for;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> <span style="color:#e6db74">https</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_http_version</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">.1</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">&#34;upgrade&#34;</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p>Enable the config and reload Nginx:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo ln -s /etc/nginx/sites-available/notes.alipourimjourneys.ir /etc/nginx/sites-enabled/
</span></span><span style="display:flex;"><span>sudo nginx -t <span style="color:#f92672">&amp;&amp;</span> sudo systemctl reload nginx</span></span></code></pre></div>
<hr>
<h2 id="6-create-users">6. Create users</h2>
<p>Because we disabled self-registration, create accounts manually:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose exec hedgedoc ./bin/manage_users --add alice@example.com</span></span></code></pre></div>
<p>Youll be prompted for a password.<br>
To reset a password later:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose exec hedgedoc ./bin/manage_users --reset alice@example.com</span></span></code></pre></div>
<hr>
<h2 id="7-done">7. Done!</h2>
<p>Visit <a href="https://notes.alipourimjourneys.ir">https://notes.alipourimjourneys.ir</a> and log in with the user you created. You now have your own collaborative Markdown editor 🎉</p>
<hr>
<p><strong>Extras:</strong></p>
<ul>
<li>Backups: dump the PostgreSQL database and save the <code>uploads</code> volume.</li>
<li>Upgrades: <code>docker pull quay.io/hedgedoc/hedgedoc:latest &amp;&amp; docker compose up -d</code>.</li>
<li>Integrations: HedgeDoc supports S3/MinIO image storage, GitHub/GitLab/Google login, and more.</li>
</ul>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/guides/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/guides/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/guides/"></head></html>

View file

@ -0,0 +1,10 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Homelab | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/homelab/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/homelab/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/homelab/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/homelab/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Homelab"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Homelab"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/>Categories</a></div><h1>Homelab
<a href=/categories/homelab/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><header class=entry-header><h2 class=entry-hint-parent>Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases</h2></header><div class=entry-content><p>How I containerized a Java Minecraft server with Geyser, hit a /var space wall, and locked the server to the latest stable release.</p></div><footer class=entry-footer><span title='2025-09-29 09:06:29 +0000 UTC'>September 29, 2025</span>&nbsp;·&nbsp;3 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,259 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Homelab on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/homelab/</link><description>Recent content in Homelab on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Mon, 29 Sep 2025 09:06:29 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/homelab/index.xml" rel="self" type="application/rss+xml"/><item><title>Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/</link><pubDate>Mon, 29 Sep 2025 09:06:29 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/</guid><description>How I containerized a Java Minecraft server with Geyser, hit a /var space wall, and locked the server to the latest stable release.</description><content:encoded><![CDATA[<h2 id="why">Why</h2>
<p>Ive been running a Java Minecraft world (with Bedrock players via <strong>Geyser</strong>) in <code>tmux</code>. I moved it to Docker for easier updates, backups, and restarts. Along the way I hit:</p>
<ul>
<li><code>failed to register layer: ... no space left on device</code></li>
<li>Client saying: <strong>“Outdated client, please use 1.21.9 Pre-Release 2”</strong></li>
<li>Wanting config in a neat <code>.env</code> and <strong>no whitelist</strong></li>
</ul>
<p>Heres exactly what I did.</p>
<hr>
<h2 id="folder-layout">Folder layout</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>~/minecraft/
</span></span><span style="display:flex;"><span>├─ docker-compose.yml
</span></span><span style="display:flex;"><span>├─ .env
</span></span><span style="display:flex;"><span>└─ plugins/
</span></span></code></pre></div><hr>
<h2 id="env-secrets--knobs"><code>.env</code> (secrets &amp; knobs)</h2>
<p>Use the <strong>latest stable</strong> (not snapshots/pre-releases) by setting <code>VERSION=LATEST</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-env" data-lang="env"><span style="display:flex;"><span><span style="color:#75715e"># Minecraft server configuration</span>
</span></span><span style="display:flex;"><span>EULA<span style="color:#f92672">=</span>TRUE
</span></span><span style="display:flex;"><span>TYPE<span style="color:#f92672">=</span>PAPER
</span></span><span style="display:flex;"><span>VERSION<span style="color:#f92672">=</span>LATEST
</span></span><span style="display:flex;"><span>MEMORY<span style="color:#f92672">=</span>4G
</span></span><span style="display:flex;"><span>USE_AIKAR_FLAGS<span style="color:#f92672">=</span>true
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># RCON (remote console)</span>
</span></span><span style="display:flex;"><span>ENABLE_RCON<span style="color:#f92672">=</span>true
</span></span><span style="display:flex;"><span>RCON_PASSWORD<span style="color:#f92672">=</span>superSecretPassword123
</span></span></code></pre></div><blockquote>
<p>I dont use a whitelist, so no <code>WHITELIST</code>/<code>ENFORCE_WHITELIST</code> variables.</p></blockquote>
<hr>
<h2 id="docker-composeyml"><code>docker-compose.yml</code></h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">mc</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">itzg/minecraft-server:latest</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">mc</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;25565:25565&#34;</span> <span style="color:#75715e"># Java</span>
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;19132:19132/udp&#34;</span> <span style="color:#75715e"># Bedrock via Geyser plugin</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">env_file</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">.env</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">mc-data:/data</span>
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">./plugins:/plugins:ro</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">mc-data</span>: {}
</span></span></code></pre></div><blockquote>
<p>The <code>version:</code> key in Compose is obsolete now, so I dropped it.</p></blockquote>
<hr>
<h2 id="add-geyser-and-optional-floodgate-as-plugins">Add Geyser (and optional Floodgate) as plugins</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir -p ~/minecraft/plugins
</span></span><span style="display:flex;"><span>cd ~/minecraft/plugins
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Geyser (Spigot/Paper)</span>
</span></span><span style="display:flex;"><span>wget https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot -O Geyser-Spigot.jar
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Floodgate (optional: Bedrock accounts without Java linking)</span>
</span></span><span style="display:flex;"><span>wget https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot -O Floodgate-Spigot.jar
</span></span></code></pre></div><p>Start it up:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose up -d
</span></span></code></pre></div><p>On first run, Geyser writes its config to <code>/data/plugins/Geyser-Spigot/</code>. Open UDP <strong>19132</strong> on your firewall.</p>
<hr>
<h2 id="hit-a-wall-var-ran-out-of-space">Hit a wall: <code>/var</code> ran out of space</h2>
<p>Docker stores layers at <code>/var/lib/docker</code> by default. My <code>/var</code> LV was tiny:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 448 25"
>
<g transform='translate(8,16)'>
<path d='M 404,8 L 404,24' fill='none' stroke='currentColor'></path>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>%</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>r</text>
</g>
</svg>
</div>
<h3 id="quick-cleanup">Quick cleanup</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker system df
</span></span><span style="display:flex;"><span>docker system prune -f
</span></span><span style="display:flex;"><span>docker builder prune -af
</span></span><span style="display:flex;"><span>sudo apt-get clean
</span></span><span style="display:flex;"><span>sudo journalctl --vacuum-size<span style="color:#f92672">=</span>100M
</span></span></code></pre></div><p>If thats not enough, you have two good options:</p>
<h3 id="option-a--move-dockers-data-off-var">Option A — Move Dockers data off <code>/var</code></h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo systemctl stop docker
</span></span><span style="display:flex;"><span>sudo mkdir -p /home/docker
</span></span><span style="display:flex;"><span>sudo rsync -aHAX --info<span style="color:#f92672">=</span>progress2 /var/lib/docker/ /home/docker/
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#39;{ &#34;data-root&#34;: &#34;/home/docker&#34; }&#39;</span> | sudo tee /etc/docker/daemon.json
</span></span><span style="display:flex;"><span>sudo systemctl start docker
</span></span><span style="display:flex;"><span>docker info | grep <span style="color:#e6db74">&#34;Docker Root Dir&#34;</span>
</span></span></code></pre></div><p>If all looks good, free the old space:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo rm -rf /var/lib/docker/*
</span></span></code></pre></div><h3 id="option-b--grow-var-lvm">Option B — Grow <code>/var</code> (LVM)</h3>
<p>Check free space in the VG:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo vgdisplay <span style="color:#75715e"># look for &#34;Free PE / Size&#34;</span>
</span></span></code></pre></div><p>If available, extend <code>/var</code> by +5G:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo lvextend -L +5G /dev/mapper/ubuntu--vg-var
</span></span><span style="display:flex;"><span>sudo resize2fs /dev/mapper/ubuntu--vg-var
</span></span></code></pre></div><hr>
<h2 id="the-version-mismatch-pre-release-vs-stable">The version mismatch: pre-release vs stable</h2>
<p>My logs showed:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 440 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>2</text>
</g>
</svg>
</div>
<p>That happens if the server pulls a pre-release build (or if <code>VERSION=LATEST</code>). Fix:</p>
<ol>
<li>Ensure <code>.env</code> has:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-env" data-lang="env"><span style="display:flex;"><span>VERSION<span style="color:#f92672">=</span>LATEST_RELEASE
</span></span></code></pre></div></li>
<li>Recreate:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose down
</span></span><span style="display:flex;"><span>docker compose pull
</span></span><span style="display:flex;"><span>docker compose up -d
</span></span></code></pre></div></li>
<li>Verify:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker logs mc | grep <span style="color:#e6db74">&#34;Starting minecraft server version&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># -&gt; Starting minecraft server version 1.21.1 (example)</span>
</span></span></code></pre></div></li>
</ol>
<blockquote>
<p>Tip: If you <strong>want to pin</strong> and avoid auto-updates entirely, set <code>VERSION=1.21.1</code> (or whatever stable youve validated).</p></blockquote>
<hr>
<h2 id="no-whitelist">No whitelist</h2>
<p>Because I dont set <code>WHITELIST</code>/<code>ENFORCE_WHITELIST</code>, anyone can join (subject to online-mode, bans, and Geyser/Floodgate auth settings). Manage ops/permissions via:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker exec -it mc rcon-cli <span style="color:#e6db74">&#34;op YourJavaIGN&#34;</span>
</span></span></code></pre></div><p>(or edit <code>/data/ops.json</code>).</p>
<hr>
<h2 id="backup--updates">Backup &amp; updates</h2>
<ul>
<li>World/data live under the <code>mc-data</code> volume → back up <code>/data</code> regularly.</li>
<li>Update cleanly:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose pull <span style="color:#f92672">&amp;&amp;</span> docker compose up -d
</span></span></code></pre></div></li>
<li>Watch space:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>watch -n5 <span style="color:#e6db74">&#39;df -h /var; docker system df&#39;</span>
</span></span></code></pre></div></li>
</ul>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Put <strong>Geyser/Floodgate</strong> jars in <code>./plugins</code> and expose <strong>19132/udp</strong>.</li>
<li>Keep configs in <code>.env</code>.</li>
<li>Fix <code>/var</code> space by pruning, <strong>moving Dockers data-root</strong>, or <strong>extending <code>/var</code></strong> via LVM.</li>
<li>Verify version in logs after each change.</li>
</ul>
<p>Happy block-breaking! 🧱🚀</p>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/homelab/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/homelab/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/homelab/"></head></html>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Categories on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/</link><description>Recent content in Categories on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Mon, 29 Sep 2025 09:06:29 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/index.xml" rel="self" type="application/rss+xml"/><item><title>Games</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/games/</link><pubDate>Mon, 29 Sep 2025 09:06:29 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/games/</guid><description/></item><item><title>Homelab</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/homelab/</link><pubDate>Mon, 29 Sep 2025 09:06:29 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/homelab/</guid><description/></item><item><title>DevOps</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/devops/</link><pubDate>Fri, 19 Sep 2025 00:00:00 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/devops/</guid><description/></item><item><title>Notes</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/notes/</link><pubDate>Fri, 19 Sep 2025 00:00:00 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/notes/</guid><description/></item><item><title>Guides</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/guides/</link><pubDate>Tue, 09 Sep 2025 11:05:00 +0200</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/guides/</guid><description/></item></channel></rss>

View file

@ -0,0 +1,14 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Notes | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/notes/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/notes/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/notes/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/notes/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Notes"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Notes"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/>Categories</a></div><h1>Notes
<a href=/categories/notes/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><header class=entry-header><h2 class=entry-hint-parent>Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod</h2></header><div class=entry-content><p>TL;DR — The site is built once (Hugo project), published twice:
Onion: http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ via Tor, no TLS/HSTS, bound to 127.0.0.1:3301. Clearnet: https://blog.alipourimjourneys.ir behind Cloudflare, Lets Encrypt cert, Onion-Location header pointing to the onion mirror. 1) Tor hidden service (onion) basics I used Tors v3 onion services and mapped onion port 80 → my local web server on 127.0.0.1:3301.
Install & configure Tor (Debian/Ubuntu):
sudo apt update && sudo apt install -y tor sudoedit /etc/tor/torrc Add:
...</p></div><footer class=entry-footer><span title='2025-09-19 00:00:00 +0000 UTC'>September 19, 2025</span>&nbsp;·&nbsp;6 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,422 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Notes on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/notes/</link><description>Recent content in Notes on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Fri, 19 Sep 2025 00:00:00 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/notes/index.xml" rel="self" type="application/rss+xml"/><item><title>Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/</link><pubDate>Fri, 19 Sep 2025 00:00:00 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/</guid><description>How I hosted a Hugo + PaperMod site both as a Tor onion service and a normal HTTPS site behind Cloudflare, with clean configs, dual builds, and a one-command deploy.</description><content:encoded><![CDATA[<blockquote>
<p>TL;DR — The site is built once (Hugo project), <strong>published twice</strong>:</p>
<ul>
<li><strong>Onion</strong>: <code>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/</code> via Tor, no TLS/HSTS, bound to <code>127.0.0.1:3301</code>.</li>
<li><strong>Clearnet</strong>: <code>https://blog.alipourimjourneys.ir</code> behind Cloudflare, Lets Encrypt cert, <code>Onion-Location</code> header pointing to the onion mirror.</li>
</ul></blockquote>
<hr>
<h2 id="1-tor-hidden-service-onion-basics">1) Tor hidden service (onion) basics</h2>
<p>I used Tors v3 onion services and mapped onion port 80 → my local web server on <code>127.0.0.1:3301</code>.</p>
<p><strong>Install &amp; configure Tor (Debian/Ubuntu):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt update <span style="color:#f92672">&amp;&amp;</span> sudo apt install -y tor
</span></span><span style="display:flex;"><span>sudoedit /etc/tor/torrc
</span></span></code></pre></div><p>Add:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 344 41"
>
<g transform='translate(8,16)'>
<path d='M 132,8 L 124,40' fill='none' stroke='currentColor'></path>
<path d='M 196,8 L 188,40' fill='none' stroke='currentColor'></path>
<path d='M 228,8 L 220,40' fill='none' stroke='currentColor'></path>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>8</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>7</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>/</text>
</g>
</svg>
</div>
<p>Make sure the directory is owned by Tors user and private:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo mkdir -p /var/lib/tor/hidden_site
</span></span><span style="display:flex;"><span>sudo chown -R debian-tor:debian-tor /var/lib/tor/hidden_site
</span></span><span style="display:flex;"><span>sudo chmod <span style="color:#ae81ff">700</span> /var/lib/tor/hidden_site
</span></span></code></pre></div><p><strong>Important:</strong> use the right systemd unit:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># validate as the Tor user</span>
</span></span><span style="display:flex;"><span>sudo -u debian-tor tor -f /etc/tor/torrc --verify-config
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># (re)start the real service</span>
</span></span><span style="display:flex;"><span>sudo systemctl enable --now tor@default
</span></span><span style="display:flex;"><span>sudo systemctl restart tor@default
</span></span></code></pre></div><p>Get the onion address:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo cat /var/lib/tor/hidden_site/hostname
</span></span></code></pre></div><p>Quick check (do remote DNS via SOCKS):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -I --socks5-hostname 127.0.0.1:9050 <span style="color:#e6db74">&#34;http://</span><span style="color:#66d9ef">$(</span>sudo cat /var/lib/tor/hidden_site/hostname<span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span>
</span></span></code></pre></div><hr>
<h2 id="2-nginx-for-the-onion-localhost-only">2) Nginx for the onion (localhost-only)</h2>
<p>I keep the onion site strictly on localhost: <strong>no HTTPS, no redirects, no HSTS</strong>. Tor already provides e2e encryption and authenticity.</p>
<p><code>/etc/nginx/sites-available/onion-blog</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> 127.0.0.1:<span style="color:#ae81ff">3301</span> <span style="color:#e6db74">default_server</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">&lt;your-56-char&gt;.onion</span> 127.0.0.1 <span style="color:#e6db74">localhost</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># keep onion simple; no HSTS/redirects here
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Referrer-Policy</span> <span style="color:#e6db74">no-referrer</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">X-Content-Type-Options</span> <span style="color:#e6db74">nosniff</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">X-Frame-Options</span> <span style="color:#e6db74">SAMEORIGIN</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># (optional) strict CSP so nothing leaks to clearnet
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e"># add_header Content-Security-Policy &#34;default-src &#39;self&#39;; img-src &#39;self&#39; data:; style-src &#39;self&#39; &#39;unsafe-inline&#39;; script-src &#39;self&#39;&#34; always;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/srv/hugo/mysite/public-onion</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">index</span> <span style="color:#e6db74">index.html</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> { <span style="color:#f92672">try_files</span> $uri $uri/ <span style="color:#e6db74">/index.html</span>; }
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> ~<span style="color:#e6db74">*</span> <span style="color:#e6db74">\.(css|js|ico|png|jpg|jpeg|gif|svg|webp|txt|xml)</span>$ {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">access_log</span> <span style="color:#66d9ef">off</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Cache-Control</span> <span style="color:#e6db74">&#34;public,</span> <span style="color:#e6db74">max-age=31536000,</span> <span style="color:#e6db74">immutable&#34;</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">try_files</span> $uri =<span style="color:#ae81ff">404</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Enable/reload:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo ln -sf /etc/nginx/sites-available/onion-blog /etc/nginx/sites-enabled/onion-blog
</span></span><span style="display:flex;"><span>sudo nginx -t <span style="color:#f92672">&amp;&amp;</span> sudo systemctl reload nginx
</span></span></code></pre></div><blockquote>
<p>Gotcha I hit: I had <strong>two</strong> server blocks on <code>127.0.0.1:3301</code>, which caused 404s via onion. Make sure only the intended vhost listens there (or mark it <code>default_server</code>). Also, if you ever use a regex in <code>server_name</code>, the syntax is <code>server_name ~* \.onion$</code> (note the space after <code>~*</code>).</p></blockquote>
<hr>
<h2 id="3-hugo--papermod-setup-and-version-bumps">3) Hugo + PaperMod setup (and version bumps)</h2>
<p>PaperMod now requires <strong>Hugo Extended ≥ 0.146.0</strong>. I installed the extended binary from the official tarball to avoid Snaps sandbox limitations (Snap cant read <code>/srv</code> paths by default).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># install Hugo extended (example)</span>
</span></span><span style="display:flex;"><span>VER<span style="color:#f92672">=</span>0.146.0
</span></span><span style="display:flex;"><span>cd /tmp
</span></span><span style="display:flex;"><span>wget https://github.com/gohugoio/hugo/releases/download/v<span style="color:#e6db74">${</span>VER<span style="color:#e6db74">}</span>/hugo_extended_<span style="color:#e6db74">${</span>VER<span style="color:#e6db74">}</span>_Linux-amd64.tar.gz
</span></span><span style="display:flex;"><span>tar -xzf hugo_extended_<span style="color:#e6db74">${</span>VER<span style="color:#e6db74">}</span>_Linux-amd64.tar.gz
</span></span><span style="display:flex;"><span>sudo mv hugo /usr/local/bin/hugo
</span></span><span style="display:flex;"><span>hugo version <span style="color:#75715e"># should say &#34;extended&#34; and &gt;= 0.146.0</span>
</span></span></code></pre></div><p>Create the site and theme:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo mkdir -p /srv/hugo <span style="color:#f92672">&amp;&amp;</span> sudo chown -R <span style="color:#e6db74">&#34;</span>$USER<span style="color:#e6db74">&#34;</span>:<span style="color:#e6db74">&#34;</span>$USER<span style="color:#e6db74">&#34;</span> /srv/hugo
</span></span><span style="display:flex;"><span>cd /srv/hugo
</span></span><span style="display:flex;"><span>hugo new site mysite
</span></span><span style="display:flex;"><span>cd mysite
</span></span><span style="display:flex;"><span>git init
</span></span><span style="display:flex;"><span>git submodule add https://github.com/adityatelange/hugo-PaperMod themes/PaperMod
</span></span></code></pre></div><p><strong>Config updates:</strong> in newer Hugo, <code>paginate</code> is deprecated → use:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span><span style="color:#75715e"># config/_default/hugo.toml</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">title</span> = <span style="color:#e6db74">&#34;My Blog&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">theme</span> = <span style="color:#e6db74">&#34;PaperMod&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">enableRobotsTXT</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">pagination</span>]
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">pagerSize</span> = <span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">params</span>]
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">defaultTheme</span> = <span style="color:#e6db74">&#34;auto&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showReadingTime</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showPostNavLinks</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showBreadCrumbs</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showCodeCopyButtons</span> = <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><p>I added per-environment overrides so I can build two outputs with different <code>baseURL</code>s:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span><span style="color:#75715e"># config/clearnet/hugo.toml</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">baseURL</span> = <span style="color:#e6db74">&#34;https://blog.alipourimjourneys.ir/&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># config/onion/hugo.toml</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">baseURL</span> = <span style="color:#e6db74">&#34;http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/&#34;</span>
</span></span></code></pre></div><hr>
<h2 id="4-dual-builds-clearnet--onion-and-one-command-deploy">4) Dual builds (clearnet + onion) and one-command deploy</h2>
<p>I publish the same content twice—once for each base URL and docroot:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd /srv/hugo/mysite
</span></span><span style="display:flex;"><span><span style="color:#75715e"># clearnet build (served over HTTPS)</span>
</span></span><span style="display:flex;"><span>hugo --minify --environment clearnet -d public-clearnet
</span></span><span style="display:flex;"><span><span style="color:#75715e"># onion build (served via Tor)</span>
</span></span><span style="display:flex;"><span>hugo --minify --environment onion -d public-onion
</span></span><span style="display:flex;"><span>sudo systemctl reload nginx
</span></span></code></pre></div><p>Helper script I use:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo tee /usr/local/bin/build-both &gt;/dev/null <span style="color:#e6db74">&lt;&lt;&#39;EOF&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">set -euo pipefail
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">cd /srv/hugo/mysite
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">hugo --minify --environment clearnet -d public-clearnet
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">hugo --minify --environment onion -d public-onion
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">sudo systemctl reload nginx
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">echo &#34;Deployed both at $(date)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span>sudo chmod +x /usr/local/bin/build-both
</span></span></code></pre></div><p>While editing, I sometimes auto-rebuild on file save:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt install -y entr
</span></span><span style="display:flex;"><span>cd /srv/hugo/mysite
</span></span><span style="display:flex;"><span>find content layouts assets static config -type f | entr -r build-both
</span></span></code></pre></div><hr>
<h2 id="5-clearnet-behind-cloudflare--lets-encrypt-manual-dns-01">5) Clearnet behind Cloudflare + Lets Encrypt (manual DNS-01)</h2>
<p>Cloudflare is set to <strong>Full (strict)</strong>. I issued a public cert for <code>blog.alipourimjourneys.ir</code> using <strong>manual DNS-01</strong> (no API token):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo snap install --classic certbot
</span></span><span style="display:flex;"><span>sudo certbot certonly --manual --preferred-challenges dns -d blog.alipourimjourneys.ir --agree-tos -m you@example.com --no-eff-email
</span></span></code></pre></div><p>Certbot tells you to add a TXT record <code>_acme-challenge.blog.alipourimjourneys.ir</code>. Add it in Cloudflare DNS, verify with <code>dig</code>, then continue. Cert ends up at:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 496 41"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='280' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='288' y='20' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='296' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='304' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='312' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='320' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='336' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='344' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='352' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='360' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='368' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='376' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='384' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='392' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='400' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='408' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='416' y='20' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='424' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='432' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='440' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='448' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='456' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='464' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>m</text>
</g>
</svg>
</div>
<p>Clearnet Nginx vhosts:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#75715e"># HTTP → HTTPS redirect (optional; CF usually talks HTTPS to origin anyway)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">80</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:80</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">blog.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">return</span> <span style="color:#ae81ff">301</span> <span style="color:#e6db74">https://blog.alipourimjourneys.ir</span>$request_uri;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># HTTPS origin (behind Cloudflare)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">blog.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/blog.alipourimjourneys.ir/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/blog.alipourimjourneys.ir/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># HSTS on clearnet only
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Strict-Transport-Security</span> <span style="color:#e6db74">&#34;max-age=31536000</span>; <span style="color:#f92672">includeSubDomains</span>; <span style="color:#f92672">preload&#34;</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Help Tor Browser discover the onion mirror (safe on clearnet)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Onion-Location</span> <span style="color:#e6db74">&#34;http://&lt;your-56-char&gt;.onion</span>$request_uri&#34; <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/srv/hugo/mysite/public-clearnet</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">index</span> <span style="color:#e6db74">index.html</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> { <span style="color:#f92672">try_files</span> $uri $uri/ <span style="color:#e6db74">/index.html</span>; }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>Note: dont add HSTS or HTTPS redirects to the <strong>onion</strong> vhost. Keep onion pure HTTP on localhost.</p></blockquote>
<hr>
<h2 id="6-troubleshooting-i-ran-into-and-fixes">6) Troubleshooting I ran into (and fixes)</h2>
<ul>
<li><strong>Tor unit confusion:</strong> <code>tor.service</code> is a tiny master; the real daemon is <code>tor@default</code>. Use that unit and verify config as <code>debian-tor</code>.</li>
<li><strong>Permissions:</strong> <code>HiddenServiceDir</code> must be owned by <code>debian-tor</code> and mode <code>700</code>.</li>
<li><strong>Mapping mismatch:</strong> If <code>HiddenServicePort 80 127.0.0.1:3301</code> is set, visit <code>http://&lt;onion&gt;/</code> (no <code>:3301</code>). If you set <code>HiddenServicePort 3301 127.0.0.1:3301</code>, you must use <code>http://&lt;onion&gt;:3301/</code>.</li>
<li><strong>Curl &amp; .onion:</strong> modern curl refuses <code>.onion</code> unless you use remote DNS via SOCKS:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -I --socks5-hostname 127.0.0.1:9050 <span style="color:#e6db74">&#34;http://&lt;onion&gt;/&#34;</span>
</span></span></code></pre></div></li>
<li><strong>Two Nginx vhosts on the same port:</strong> I had a duplicate server on <code>127.0.0.1:3301</code> pointing somewhere else, which caused onion 404s. Keep only one (or mark one <code>default_server</code>).</li>
<li><strong>Regex in <code>server_name</code>:</strong> if you use it, write <code>server_name ~* \.onion$</code> (space after <code>~*</code>). I fixed an <code>invalid variable name</code> error caused by a missing space.</li>
<li><strong>PaperMod with old Hugo:</strong> upgraded to <strong>extended ≥ 0.146.0</strong>. Also updated <code>paginate</code><code>[pagination].pagerSize</code>.</li>
<li><strong>Snap confinement:</strong> Snaps <code>hugo</code> couldnt read <code>/srv</code> (<code>.../void: permission denied</code>). Switched to the tarball build in <code>/usr/local/bin</code>.</li>
</ul>
<hr>
<h2 id="7-not-secure-in-tor-browser">7) “Not secure” in Tor Browser?</h2>
<p>That message can appear because onion uses <strong>HTTP</strong>. Its OK: Tor provides e2e encryption + onion auth. If you enable HTTPS-Only Mode, add an exception for the site. Also ensure the onion build doesnt reference <strong>clearnet</strong> resources (scan the built HTML for <code>http(s)://</code> links that arent your onion).</p>
<hr>
<h2 id="8-day-to-day-workflow">8) Day-to-day workflow</h2>
<ul>
<li>Create content:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>hugo new posts/my-first-post.md <span style="color:#75715e"># then set draft: false</span>
</span></span></code></pre></div></li>
<li>Publish both:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>build-both
</span></span></code></pre></div></li>
<li>(Optional) Auto on save:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>find content layouts assets static config -type f | entr -r build-both
</span></span></code></pre></div></li>
<li>(Optional) Git push-to-deploy with a bare repo + post-receive hook that runs <code>build-both</code>.</li>
</ul>
<hr>
<h2 id="final-notes">Final notes</h2>
<ul>
<li>Clearnet gets <strong>HTTPS + HSTS</strong> and an <code>Onion-Location</code> header.</li>
<li>Onion gets <strong>no HSTS/redirects</strong>, and all assets are self-hosted to avoid mixed content.</li>
<li>Serving both worlds from one Hugo repo is easy: <strong>two builds, two vhosts, one workflow</strong>.</li>
</ul>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/notes/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/notes/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/notes/"></head></html>

0
public/favicon.png Normal file
View file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 916 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

38
public/index.html Normal file

File diff suppressed because one or more lines are too long

1
public/index.json Normal file

File diff suppressed because one or more lines are too long

2645
public/index.xml Normal file

File diff suppressed because it is too large Load diff

2
public/page/1/index.html Normal file
View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/"></head></html>

15
public/page/2/index.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,14 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>PhD_journeys | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content="PhD_journeys - AlipourIm journeys"><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="PhD_journeys"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="PhD_journeys"><meta name=twitter:description content><script type=application/ld+json>{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"PhD_journeys","item":"http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/"}]}</script></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span class=active>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a></div><h1>PhD_journeys
<a href=/phd_journey/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class=post-entry><header class=entry-header><h2 class=entry-hint-parent>September '25</h2></header><div class=entry-content><p>I started the month by finalizing my draft for Conext Student workshop. Lets cross our fingers and hope things work out and that it gets accepted. Notification should arrive on 25th, and Id have until 30th to do the camera ready stuff which should be plenty of time.
I did a vacation from 6th until 15th for a total of 10 days by taking 6 days off. I visited my parents after 13 months(!), and also my brother after more than two years. We had so much fun! I did a bunch of sight-seeing in Istanbul, tried out yummy foods and sweets, many different fishes, and snorkled in Antalya. What a delightful trip it was. I do have to get used to this as this is the sole way I will most probably see my parents from now on, unless they want to travel to somewhere else or visit me here. Now that my brother also has his greencard, we can travel together and see eachother more often too!
...</p></div><footer class=entry-footer><span title='2025-09-30 09:48:21 +0000 UTC'>September 30, 2025</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/september_2025/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to September '25" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/september_2025/></a></article><article class=post-entry><header class=entry-header><h2 class=entry-hint-parent>Augest '25</h2></header><div class=entry-content><p>This month started with me setting up a deadline for Conext student workshop. I wanted to submit something not only to get the chance to attend a nice conference, but to create a network, meet researchers, and to get a chance to present my work and get feedback on it. I also worked on my code-base, finally making everything work by replacing Jool with clatd for performing CxLAT. This darn thing wasted so much of my time! I did learn a bit along the way, but oh well. Such is life!
...</p></div><footer class=entry-footer><span title='2025-08-31 07:49:24 +0000 UTC'>August 31, 2025</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/augest_2025/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Augest '25" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/augest_2025/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>PhD_journeys on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/</link><description>Recent content in PhD_journeys on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Tue, 30 Sep 2025 09:48:21 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/index.xml" rel="self" type="application/rss+xml"/><item><title>September '25</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/september_2025/</link><pubDate>Tue, 30 Sep 2025 09:48:21 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/september_2025/</guid><description>&lt;p>I started the month by finalizing my draft for Conext Student workshop.
Let&amp;rsquo;s cross our fingers and hope things work out and that it gets accepted.
Notification should arrive on 25th, and I&amp;rsquo;d have until 30th to do the camera ready stuff which should be plenty of time.&lt;/p>
&lt;p>I did a vacation from 6th until 15th for a total of 10 days by taking 6 days off.
I visited my parents after 13 months(!), and also my brother after more than two years.
We had so much fun!
I did a bunch of sight-seeing in Istanbul, tried out yummy foods and sweets, many different fishes, and snorkled in Antalya.
What a delightful trip it was.
I do have to get used to this as this is the sole way I will most probably see my parents from now on, unless they want to travel to somewhere else or visit me here.
Now that my brother also has his greencard, we can travel together and see eachother more often too!&lt;/p></description><content:encoded><![CDATA[<p>I started the month by finalizing my draft for Conext Student workshop.
Let&rsquo;s cross our fingers and hope things work out and that it gets accepted.
Notification should arrive on 25th, and I&rsquo;d have until 30th to do the camera ready stuff which should be plenty of time.</p>
<p>I did a vacation from 6th until 15th for a total of 10 days by taking 6 days off.
I visited my parents after 13 months(!), and also my brother after more than two years.
We had so much fun!
I did a bunch of sight-seeing in Istanbul, tried out yummy foods and sweets, many different fishes, and snorkled in Antalya.
What a delightful trip it was.
I do have to get used to this as this is the sole way I will most probably see my parents from now on, unless they want to travel to somewhere else or visit me here.
Now that my brother also has his greencard, we can travel together and see eachother more often too!</p>
<p>Surprise surprize, the notification did not come and it&rsquo;s the last day of the month.
Ever since I came back from vacation I tried to catch up with everything, did my ML re-exam, and setup all different cool things I talked about in my blog.
I&rsquo;ve been waiting for the notification to get started with the camera-ready process to make sure I have enough time to study for my next and last exam on 7th of October&hellip;
Drat!</p>
<p>I will probably do more literature review this last day of the month, and start working on the code base from next month.
I should do a lot more literature review to be caught up with whatever that&rsquo;s been done so far.</p>
<p>My social life has been much more exciting too.
I&rsquo;ve been socializing a lot more and have made many new friends.
Some other exciting things have also been hapening that I don&rsquo;t have the courage to write about now. ;)
Buuuuuut&hellip; I will probably write about them at some point if things go the way I hope theuy would.
Just remember Wed 24th of September 2025 and Sunday 28th of same month and year. :)</p>
<p>And with that, that&rsquo;s my September in a nutshell.
I will probably start writing through the month and then turn the draft into a post from now on.
That way it would look like a story!</p>
]]></content:encoded></item><item><title>Augest '25</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/augest_2025/</link><pubDate>Sun, 31 Aug 2025 07:49:24 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/augest_2025/</guid><description>&lt;p>This month started with me setting up a deadline for Conext student workshop.
I wanted to submit something not only to get the chance to attend a nice conference, but to create a network, meet researchers, and to get a chance to present my work and get feedback on it.
I also worked on my code-base, finally making everything work by replacing Jool with clatd for performing CxLAT.
This darn thing wasted so much of my time!
I did learn a bit along the way, but oh well.
Such is life!&lt;/p></description><content:encoded><![CDATA[<p>This month started with me setting up a deadline for Conext student workshop.
I wanted to submit something not only to get the chance to attend a nice conference, but to create a network, meet researchers, and to get a chance to present my work and get feedback on it.
I also worked on my code-base, finally making everything work by replacing Jool with clatd for performing CxLAT.
This darn thing wasted so much of my time!
I did learn a bit along the way, but oh well.
Such is life!</p>
<p>Overall not the most productive month, but one reason for it is that I have&rsquo;t really had a real vacation in a long time.
I will be taking a 10 day vacation next month just to reset, and gain back my power.
I cross my fingers for the month ahead!</p>
<p>My social life has been becoming better too.
I&rsquo;ve been trying to attend more ZiS events to meet people, make new connections, and to have fun!
My depression is a serious issue.
I also have anxiety disorder that I&rsquo;m talking with my therapist about.
I will most likely start taking SSRIs once again.
Idiot me was cutting it when the catasrophe in February happened and did not think twice to keep taking them.
I&rsquo;m really glad I was able to recover, though it was not easy at all, it did work out.</p>
<p>I do think there is bright nice future ahead of me; I just need to keep on trucking and not worry too much.
Things will workout!</p>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/"></head></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

40
public/posts/index.html Normal file
View file

@ -0,0 +1,40 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Posts | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content="Posts - AlipourIm journeys"><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Posts"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Posts"><meta name=twitter:description content><script type=application/ld+json>{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Posts","item":"http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/"}]}</script></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span class=active>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a></div><h1>Posts
<a href=/posts/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class=post-entry><header class=entry-header><h2 class=entry-hint-parent>Skin routine</h2></header><div class=entry-content><p>I dont know how to answer the “why?” or “whyyyyy?” or even “whe the f***?” I have a skin routine. Last year, after I came to Germany, I asked a female friend about how to do skin care. She touched my face and said “Knock on wood, you have good skin!”. So… idk why I decided to take extra care of my skin, but I did!
Generaly speaking, things like this make me feel good about myself. Like Im doing something positive while not being tortured! Its always fun to rub a creme on your face, or gently message it. Even cleaning the face skin feels refreshing. Everything also smells nice!
...</p></div><footer class=entry-footer><span title='2025-10-20 18:47:04 +0000 UTC'>October 20, 2025</span>&nbsp;·&nbsp;3 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/skin_routine/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Skin routine" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/skin_routine/></a></article><article class=post-entry><figure class=entry-cover><img loading=lazy src=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/images/inet/inet_animation_collage.jpg alt="Six looks of the INET LED logo"></figure><header class=entry-header><h2 class=entry-hint-parent>INET Logo That Breathes — From CAD to LEDs to a Calm Little Server</h2></header><div class=entry-content><p>I didnt add a warning sign to the room. I taught the logo to breathe. ;-D
The Rotunde is a good room for bold ideas and yummy coffee. The door sighs shut and the outside world gives up on us. The only thing its not good at is ventilating itself. Forty minutes into a group meeting, or a sressful defence, and we all feel like sleeping and running back to our offices!
...</p></div><footer class=entry-footer><span title='2025-10-17 00:00:00 +0000 UTC'>October 17, 2025</span>&nbsp;·&nbsp;16 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/inet_logo/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to INET Logo That Breathes — From CAD to LEDs to a Calm Little Server" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/inet_logo/></a></article><article class=post-entry><header class=entry-header><h2 class=entry-hint-parent>Movie review: Scent of a Woman</h2></header><div class=entry-content><p>Im not a big movie watcher. In fact, I dont remember the last time I watched a whole movie in a single day. Its not that I dont enjoy it, its that I want to do this with at least another person. It feels much better to not be alone when watching a movie for me for some reason. Buuuut, I watched Scent of a Woman on Sunday, and after my therapist encouraged me to do so.
...</p></div><footer class=entry-footer><span title='2025-10-13 08:52:10 +0000 UTC'>October 13, 2025</span>&nbsp;·&nbsp;5 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/scent_of_a_woman/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Movie review: Scent of a Woman" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/scent_of_a_woman/></a></article><article class=post-entry><header class=entry-header><h2 class=entry-hint-parent>Hobbies</h2></header><div class=entry-content><p>When I started my PhD, I was told “get yourself a hobby!” many many times by both my advisor, and the director of the group I worked in. In fact, when being interviewed for the position, I was asked about my hobbies.
For some reason I think I have like the weirdest hobbies of all time. Like, you know, people binge series, go to clubs, bars, hang out, and so on. But I always had interests in things that when I talk about people get weirded out, or at least some of them do so. To be honest though, when I talk more about some of my hobbies, they do show enthusiasm, but it feels like there is a clear gap between us afterwards.
...</p></div><footer class=entry-footer><span title='2025-10-10 07:04:54 +0000 UTC'>October 10, 2025</span>&nbsp;·&nbsp;11 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hobbies/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Hobbies" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hobbies/></a></article><article class=post-entry><header class=entry-header><h2 class=entry-hint-parent>Relationships</h2></header><div class=entry-content><p>Notice: Youll hear me ranting in this post, so buckle up, or just go to another fun post. I am very vulnerable right now and want to put out my story without naming anyone.
So… do you also think youre a lover boy, kind, nice person? So do I! I think Im actually really good at making friends with people. Like you know, you start introducing yourself, asking them some fun personal questions about hobbies or other stuff, and then you eventually start talking about other things.
...</p></div><footer class=entry-footer><span title='2025-10-05 08:03:31 +0000 UTC'>October 5, 2025</span>&nbsp;·&nbsp;10 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/relationships/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Relationships" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/relationships/></a></article><article class=post-entry><header class=entry-header><h2 class=entry-hint-parent>Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases</h2></header><div class=entry-content><p>How I containerized a Java Minecraft server with Geyser, hit a /var space wall, and locked the server to the latest stable release.</p></div><footer class=entry-footer><span title='2025-09-29 09:06:29 +0000 UTC'>September 29, 2025</span>&nbsp;·&nbsp;3 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/></a></article><article class=post-entry><header class=entry-header><h2 class=entry-hint-parent>Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod</h2></header><div class=entry-content><p>TL;DR — The site is built once (Hugo project), published twice:
Onion: http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ via Tor, no TLS/HSTS, bound to 127.0.0.1:3301. Clearnet: https://blog.alipourimjourneys.ir behind Cloudflare, Lets Encrypt cert, Onion-Location header pointing to the onion mirror. 1) Tor hidden service (onion) basics I used Tors v3 onion services and mapped onion port 80 → my local web server on 127.0.0.1:3301.
Install & configure Tor (Debian/Ubuntu):
sudo apt update && sudo apt install -y tor sudoedit /etc/tor/torrc Add:
...</p></div><footer class=entry-footer><span title='2025-09-19 00:00:00 +0000 UTC'>September 19, 2025</span>&nbsp;·&nbsp;6 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/></a></article><article class=post-entry><header class=entry-header><h2 class=entry-hint-parent>Stealth Trojan VPN Behind Cloudflare Guide</h2></header><div class=entry-content><p>Introduction So you want a VPN that doesnt scream “I am a VPN” to every censor and firewall out there?
Welcome to the world of Trojan over WebSocket + TLS behind Cloudflare.
This guide not only shows you how to set it up but also sprinkles in some debugging magic so you can figure out why things break (and they will break, trust me).
Well anonymise domains and secrets, so substitute with your own:
...</p></div><footer class=entry-footer><span title='2025-09-09 11:05:00 +0200 +0200'>September 9, 2025</span>&nbsp;·&nbsp;4 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Stealth Trojan VPN Behind Cloudflare Guide" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/></a></article><article class=post-entry><figure class=entry-cover><img loading=lazy src=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/images/hedgedoc-cover.png alt="HedgeDoc collaborative editing"></figure><header class=entry-header><h2 class=entry-hint-parent>Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt</h2></header><div class=entry-content><p>HedgeDoc is an open-source collaborative Markdown editor. Think Google Docs for Markdown: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post well walk through setting up your own instance on a server, secured with HTTPS.
Prerequisites A Linux server with Docker and Docker Compose A domain name pointing to your server (e.g. notes.alipourimjourneys.ir) Nginx installed for reverse proxying Certbot for Lets Encrypt certificates 1. Create the project directory mkdir ~/hedgedoc && cd ~/hedgedoc 2. Create .env POSTGRES_PASSWORD=ChangeThisStrongPassword HD_DOMAIN=notes.alipourimjourneys.ir Generate a strong password with:
...</p></div><footer class=entry-footer><span title='2025-08-29 00:00:00 +0000 UTC'>August 29, 2025</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/></a></article><article class=post-entry><figure class=entry-cover><img loading=lazy src=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/images/matrix-cover.png alt="Matrix, Signal but distributed"></figure><header class=entry-header><h2 class=entry-hint-parent>Self-hosting Matrix + Element Call with LiveKit: from zero to working (and the [not so!!] fun debugging along the way)</h2></header><div class=entry-content><p>TL;DR: The call kept saying “waiting for media” because the browser never opened a WebSocket to LiveKit. The root cause was duplicate Access-Control-Allow-Origin headers on /sfu/get (CORS), which stopped the JWT response. Fixing CORS and ensuring the WS proxy worked (HTTP 101 in logs) solved it.
What Im building A Matrix homeserver (Synapse) at matrix.example.com (replace with your domain). TURN/STUN (coTURN) for NAT traversal. Element Call backed by LiveKit, fronted by rtc.example.com. Nginx (host) as the single reverse proxy for everything. Cloudflare DNS (with rtc.* set to DNS-only, no orange cloud). UFW firewall opened for Matrix federation, TURN, and LiveKit media ports. I used Docker for Synapse, PostgreSQL, LiveKit and the JWT helper. I used host Nginx (not Nginx in Docker) to avoid port binding conflicts on 80/443/8448.
...</p></div><footer class=entry-footer><span title='2025-08-26 00:00:00 +0000 UTC'>August 26, 2025</span>&nbsp;·&nbsp;9 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/matrix_setup/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Self-hosting Matrix + Element Call with LiveKit: from zero to working (and the [not so!!] fun debugging along the way)" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/matrix_setup/></a></article><footer class=page-footer><nav class=pagination><a class=next href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/page/2/>Next&nbsp;&nbsp;»</a></nav></footer></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

2589
public/posts/index.xml Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/"></head></html>

View file

@ -0,0 +1,10 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Posts | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content="Posts - AlipourIm journeys"><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Posts"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Posts"><meta name=twitter:description content><script type=application/ld+json>{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Posts","item":"http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/"}]}</script></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span class=active>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a></div><h1>Posts
<a href=/posts/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class=post-entry><header class=entry-header><h2 class=entry-hint-parent>Hello World</h2></header><div class=entry-content><p>This is a test hello world post just to make sure everything works!</p></div><footer class=entry-footer><span title='2025-08-25 08:53:38 +0000 UTC'>August 25, 2025</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hello-world/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Hello World" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hello-world/></a></article><footer class=page-footer><nav class=pagination><a class=prev href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/>«&nbsp;Prev&nbsp;</a></nav></footer></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

3
public/robots.txt Normal file
View file

@ -0,0 +1,3 @@
User-agent: *
Disallow:
Sitemap: http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/sitemap.xml

1
public/sitemap.xml Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,14 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>3x-Ui | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/3x-ui/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/3x-ui/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/3x-ui/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/3x-ui/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="3x-Ui"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="3x-Ui"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/>Tags</a></div><h1>3x-Ui
<a href=/tags/3x-ui/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><header class=entry-header><h2 class=entry-hint-parent>Stealth Trojan VPN Behind Cloudflare Guide</h2></header><div class=entry-content><p>Introduction So you want a VPN that doesnt scream “I am a VPN” to every censor and firewall out there?
Welcome to the world of Trojan over WebSocket + TLS behind Cloudflare.
This guide not only shows you how to set it up but also sprinkles in some debugging magic so you can figure out why things break (and they will break, trust me).
Well anonymise domains and secrets, so substitute with your own:
...</p></div><footer class=entry-footer><span title='2025-09-09 11:05:00 +0200 +0200'>September 9, 2025</span>&nbsp;·&nbsp;4 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Stealth Trojan VPN Behind Cloudflare Guide" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

377
public/tags/3x-ui/index.xml Normal file
View file

@ -0,0 +1,377 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>3x-Ui on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/3x-ui/</link><description>Recent content in 3x-Ui on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Tue, 09 Sep 2025 11:05:00 +0200</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/3x-ui/index.xml" rel="self" type="application/rss+xml"/><item><title>Stealth Trojan VPN Behind Cloudflare Guide</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/</link><pubDate>Tue, 09 Sep 2025 11:05:00 +0200</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>So you want a VPN that &lt;strong>doesn&amp;rsquo;t scream &amp;ldquo;I am a VPN&amp;rdquo;&lt;/strong> to every censor and firewall out there?&lt;br>
Welcome to the world of &lt;strong>Trojan over WebSocket + TLS behind Cloudflare&lt;/strong>.&lt;/p>
&lt;p>This guide not only shows you how to set it up but also sprinkles in some &lt;strong>debugging magic&lt;/strong> so you can figure out why things break (and they &lt;em>will&lt;/em> break, trust me).&lt;/p>
&lt;p>Well anonymise domains and secrets, so substitute with your own:&lt;/p></description><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>So you want a VPN that <strong>doesn&rsquo;t scream &ldquo;I am a VPN&rdquo;</strong> to every censor and firewall out there?<br>
Welcome to the world of <strong>Trojan over WebSocket + TLS behind Cloudflare</strong>.</p>
<p>This guide not only shows you how to set it up but also sprinkles in some <strong>debugging magic</strong> so you can figure out why things break (and they <em>will</em> break, trust me).</p>
<p>Well anonymise domains and secrets, so substitute with your own:</p>
<ul>
<li>VPN domain: <code>web.example.com</code></li>
<li>Panel domain: <code>panel.example.com</code></li>
<li>Secret WS path: <code>/stealth-path_abcd1234</code></li>
<li>Password: <code>&lt;PASSWORD&gt;</code></li>
</ul>
<hr>
<h2 id="architecture-at-a-glance">Architecture at a Glance</h2>
<p>Think of it as a disguise party:</p>
<ul>
<li><strong>Trojan</strong> = the shy guest (your VPN protocol)</li>
<li><strong>Nginx</strong> = the bouncer checking IDs (reverse proxy)</li>
<li><strong>Cloudflare</strong> = the doorman who makes sure nobody sees who&rsquo;s inside (CDN &amp; proxy)</li>
<li><strong>Your fake website</strong> = the mask (camouflage page)</li>
</ul>
<p>Traffic flow:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 536 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'></text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'></text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>N</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'></text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>5</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='504' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='512' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='520' y='4' fill='currentColor' style='font-size:1em'>)</text>
</g>
</svg>
</div>
<hr>
<h2 id="step-1-panel-setup-panelexamplecom">Step 1: Panel Setup (<code>panel.example.com</code>)</h2>
<ul>
<li>Bind the 3x-ui panel to localhost (e.g., <code>127.0.0.1:46309</code>).</li>
<li>Choose a funky <strong>web base path</strong> like <code>/panel-bananas_42/</code>.</li>
<li>Proxy it through Nginx with HTTPS + Basic Auth.</li>
<li>Test it at <code>https://panel.example.com/panel-bananas_42/</code>.</li>
</ul>
<p><strong>Pro tip:</strong> If you see a blank page → your base path is mismatched or Nginx is eating it. Check logs!</p>
<hr>
<h2 id="step-2-trojan-inbound">Step 2: Trojan Inbound</h2>
<p>In 3x-ui, create a Trojan inbound:</p>
<ul>
<li>Local port: <code>54321</code></li>
<li>Transport: WebSocket</li>
<li>Path: <code>/stealth-path_abcd1234</code></li>
<li>Security: none</li>
<li>Password: <code>&lt;PASSWORD&gt;</code></li>
</ul>
<hr>
<h2 id="step-3-nginx-for-vpn-domain-webexamplecom">Step 3: Nginx for VPN Domain (<code>web.example.com</code>)</h2>
<p>Your Nginx is the gatekeeper. Sample config:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">web.example.com</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/web.example.com/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/web.example.com/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Fake website at root
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/var/www/html</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">index</span> <span style="color:#e6db74">index.html</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Real VPN under secret WS path
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/stealth-path_abcd1234</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:54321</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_http_version</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">.1</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">&#34;upgrade&#34;</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><hr>
<h2 id="step-4-firewall-rules">Step 4: Firewall Rules</h2>
<p>Lock things down! Only Cloudflare should reach you:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 22/tcp
</span></span><span style="display:flex;"><span>ufw allow 80/tcp
</span></span><span style="display:flex;"><span>ufw allow 443/tcp
</span></span><span style="display:flex;"><span>ufw deny 54321/tcp
</span></span></code></pre></div><hr>
<h2 id="step-5-client-config">Step 5: Client Config</h2>
<p>Trojan URI:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 1160 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>&lt;</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>W</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>&gt;</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>@</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>?</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='504' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='512' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='520' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='528' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='536' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='544' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='552' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='560' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='568' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='576' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='584' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='592' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='600' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='608' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='616' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='624' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='632' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='640' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='648' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='656' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='664' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='672' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='680' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='688' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='696' y='4' fill='currentColor' style='font-size:1em'>%</text>
<text text-anchor='middle' x='704' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='712' y='4' fill='currentColor' style='font-size:1em'>F</text>
<text text-anchor='middle' x='720' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='728' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='736' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='744' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='752' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='760' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='768' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='776' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='784' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='792' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='800' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='808' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='816' y='4' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='824' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='832' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='840' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='848' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='856' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='864' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='872' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='880' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='888' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='896' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='904' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='912' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='920' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='928' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='936' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='944' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='952' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='960' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='968' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='976' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='984' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='992' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='1000' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='1008' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='1016' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='1024' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='1032' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='1040' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='1048' y='4' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='1056' y='4' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='1064' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='1072' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='1080' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='1088' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='1096' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='1104' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='1112' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='1120' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='1128' y='4' fill='currentColor' style='font-size:1em'>V</text>
<text text-anchor='middle' x='1136' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='1144' y='4' fill='currentColor' style='font-size:1em'>N</text>
</g>
</svg>
</div>
<p>iOS clients: Shadowrocket, Stash, FoXray<br>
Android clients: v2rayNG, Clash Meta, NekoBox</p>
<hr>
<h2 id="debugging-time-aka-why-the-heck-doesnt-it-work">Debugging Time (a.k.a. “Why the heck doesnt it work?!”)</h2>
<h3 id="symptom-520-unknown-error-cloudflare">Symptom: <strong>520 Unknown Error (Cloudflare)</strong></h3>
<ul>
<li>Likely cause: Nginx couldnt talk to Trojan.</li>
<li>Check Nginx error log:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>tail -n <span style="color:#ae81ff">50</span> /var/log/nginx/error.log
</span></span></code></pre></div></li>
<li>If you see <code>upstream sent no valid HTTP/1.0 header</code> → youre mixing HTTPS/HTTP between Nginx and Trojan.</li>
</ul>
<hr>
<h3 id="symptom-526-invalid-ssl-certificate">Symptom: <strong>526 Invalid SSL certificate</strong></h3>
<ul>
<li>Cloudflare → Nginx cert mismatch.</li>
<li>Run:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>openssl s_client -connect web.example.com:443 -servername web.example.com -showcerts
</span></span></code></pre></div></li>
<li>Make sure CN = <code>web.example.com</code>. If not, fix your cert.</li>
</ul>
<hr>
<h3 id="symptom-blank-page-on-panel">Symptom: <strong>Blank page on panel</strong></h3>
<ul>
<li>Check if the base path matches exactly (case-sensitive, slash-sensitive).</li>
<li>Watch for sneaky trailing spaces!</li>
<li>Test locally:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -s -D- http://127.0.0.1:46309/panel-bananas_42/
</span></span></code></pre></div></li>
</ul>
<hr>
<h3 id="symptom-client-wont-connect">Symptom: <strong>Client wont connect</strong></h3>
<ul>
<li>Run a WebSocket test:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -i -k -H <span style="color:#e6db74">&#34;Connection: Upgrade&#34;</span> -H <span style="color:#e6db74">&#34;Upgrade: websocket&#34;</span> https://web.example.com/stealth-path_abcd1234
</span></span></code></pre></div>Expect <code>101 Switching Protocols</code>. If not, check Nginx config.</li>
</ul>
<hr>
<h3 id="symptom-censor-still-blocks-you">Symptom: <strong>Censor still blocks you</strong></h3>
<ul>
<li>Did you expose another service (like SSH, Matrix, or Minecraft) on the same IP?<br>
→ Theyll find your origin IP. Use a second VPS or lock those ports down.</li>
<li>Did you use an obvious path like <code>/ws</code>?<br>
→ Use a random-looking one like <code>/cdn-assets-329df/</code>.</li>
</ul>
<hr>
<h2 id="pro-tips--fun-tricks">Pro Tips &amp; Fun Tricks</h2>
<ul>
<li>Serve a fake blog or portfolio at root so your domain looks legit.</li>
<li>Rotate WebSocket paths occasionally.</li>
<li>Use Cloudflare <strong>Page Rules</strong> to disable Rocket Loader &amp; Minify for your VPN domain.</li>
<li>Make friends with your Nginx error.log — it will roast you but it tells the truth.</li>
</ul>
<hr>
<h2 id="conclusion">Conclusion</h2>
<p>By putting Trojan behind Cloudflare, youve given your VPN a shiny new disguise:</p>
<ul>
<li>Looks like normal HTTPS.</li>
<li>Hides your origin IP.</li>
<li>Forces censors into a tough choice: block Cloudflare (and half the web) or let you pass.</li>
</ul>
<p>Congrats — youve built a VPN with both <strong>style</strong> and <strong>stealth</strong>. 🥷</p>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/3x-ui/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/3x-ui/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/3x-ui/"></head></html>

View file

@ -0,0 +1,14 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Bypass | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/bypass/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/bypass/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/bypass/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/bypass/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Bypass"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Bypass"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/>Tags</a></div><h1>Bypass
<a href=/tags/bypass/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><header class=entry-header><h2 class=entry-hint-parent>Stealth Trojan VPN Behind Cloudflare Guide</h2></header><div class=entry-content><p>Introduction So you want a VPN that doesnt scream “I am a VPN” to every censor and firewall out there?
Welcome to the world of Trojan over WebSocket + TLS behind Cloudflare.
This guide not only shows you how to set it up but also sprinkles in some debugging magic so you can figure out why things break (and they will break, trust me).
Well anonymise domains and secrets, so substitute with your own:
...</p></div><footer class=entry-footer><span title='2025-09-09 11:05:00 +0200 +0200'>September 9, 2025</span>&nbsp;·&nbsp;4 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Stealth Trojan VPN Behind Cloudflare Guide" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,377 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Bypass on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/bypass/</link><description>Recent content in Bypass on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Tue, 09 Sep 2025 11:05:00 +0200</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/bypass/index.xml" rel="self" type="application/rss+xml"/><item><title>Stealth Trojan VPN Behind Cloudflare Guide</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/</link><pubDate>Tue, 09 Sep 2025 11:05:00 +0200</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>So you want a VPN that &lt;strong>doesn&amp;rsquo;t scream &amp;ldquo;I am a VPN&amp;rdquo;&lt;/strong> to every censor and firewall out there?&lt;br>
Welcome to the world of &lt;strong>Trojan over WebSocket + TLS behind Cloudflare&lt;/strong>.&lt;/p>
&lt;p>This guide not only shows you how to set it up but also sprinkles in some &lt;strong>debugging magic&lt;/strong> so you can figure out why things break (and they &lt;em>will&lt;/em> break, trust me).&lt;/p>
&lt;p>Well anonymise domains and secrets, so substitute with your own:&lt;/p></description><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>So you want a VPN that <strong>doesn&rsquo;t scream &ldquo;I am a VPN&rdquo;</strong> to every censor and firewall out there?<br>
Welcome to the world of <strong>Trojan over WebSocket + TLS behind Cloudflare</strong>.</p>
<p>This guide not only shows you how to set it up but also sprinkles in some <strong>debugging magic</strong> so you can figure out why things break (and they <em>will</em> break, trust me).</p>
<p>Well anonymise domains and secrets, so substitute with your own:</p>
<ul>
<li>VPN domain: <code>web.example.com</code></li>
<li>Panel domain: <code>panel.example.com</code></li>
<li>Secret WS path: <code>/stealth-path_abcd1234</code></li>
<li>Password: <code>&lt;PASSWORD&gt;</code></li>
</ul>
<hr>
<h2 id="architecture-at-a-glance">Architecture at a Glance</h2>
<p>Think of it as a disguise party:</p>
<ul>
<li><strong>Trojan</strong> = the shy guest (your VPN protocol)</li>
<li><strong>Nginx</strong> = the bouncer checking IDs (reverse proxy)</li>
<li><strong>Cloudflare</strong> = the doorman who makes sure nobody sees who&rsquo;s inside (CDN &amp; proxy)</li>
<li><strong>Your fake website</strong> = the mask (camouflage page)</li>
</ul>
<p>Traffic flow:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 536 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'></text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'></text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>N</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'></text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>5</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='504' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='512' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='520' y='4' fill='currentColor' style='font-size:1em'>)</text>
</g>
</svg>
</div>
<hr>
<h2 id="step-1-panel-setup-panelexamplecom">Step 1: Panel Setup (<code>panel.example.com</code>)</h2>
<ul>
<li>Bind the 3x-ui panel to localhost (e.g., <code>127.0.0.1:46309</code>).</li>
<li>Choose a funky <strong>web base path</strong> like <code>/panel-bananas_42/</code>.</li>
<li>Proxy it through Nginx with HTTPS + Basic Auth.</li>
<li>Test it at <code>https://panel.example.com/panel-bananas_42/</code>.</li>
</ul>
<p><strong>Pro tip:</strong> If you see a blank page → your base path is mismatched or Nginx is eating it. Check logs!</p>
<hr>
<h2 id="step-2-trojan-inbound">Step 2: Trojan Inbound</h2>
<p>In 3x-ui, create a Trojan inbound:</p>
<ul>
<li>Local port: <code>54321</code></li>
<li>Transport: WebSocket</li>
<li>Path: <code>/stealth-path_abcd1234</code></li>
<li>Security: none</li>
<li>Password: <code>&lt;PASSWORD&gt;</code></li>
</ul>
<hr>
<h2 id="step-3-nginx-for-vpn-domain-webexamplecom">Step 3: Nginx for VPN Domain (<code>web.example.com</code>)</h2>
<p>Your Nginx is the gatekeeper. Sample config:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">web.example.com</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/web.example.com/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/web.example.com/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Fake website at root
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/var/www/html</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">index</span> <span style="color:#e6db74">index.html</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Real VPN under secret WS path
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/stealth-path_abcd1234</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:54321</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_http_version</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">.1</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">&#34;upgrade&#34;</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><hr>
<h2 id="step-4-firewall-rules">Step 4: Firewall Rules</h2>
<p>Lock things down! Only Cloudflare should reach you:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 22/tcp
</span></span><span style="display:flex;"><span>ufw allow 80/tcp
</span></span><span style="display:flex;"><span>ufw allow 443/tcp
</span></span><span style="display:flex;"><span>ufw deny 54321/tcp
</span></span></code></pre></div><hr>
<h2 id="step-5-client-config">Step 5: Client Config</h2>
<p>Trojan URI:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 1160 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>&lt;</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>W</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>&gt;</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>@</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>?</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='504' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='512' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='520' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='528' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='536' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='544' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='552' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='560' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='568' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='576' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='584' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='592' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='600' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='608' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='616' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='624' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='632' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='640' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='648' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='656' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='664' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='672' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='680' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='688' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='696' y='4' fill='currentColor' style='font-size:1em'>%</text>
<text text-anchor='middle' x='704' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='712' y='4' fill='currentColor' style='font-size:1em'>F</text>
<text text-anchor='middle' x='720' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='728' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='736' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='744' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='752' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='760' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='768' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='776' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='784' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='792' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='800' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='808' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='816' y='4' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='824' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='832' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='840' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='848' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='856' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='864' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='872' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='880' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='888' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='896' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='904' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='912' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='920' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='928' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='936' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='944' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='952' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='960' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='968' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='976' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='984' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='992' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='1000' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='1008' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='1016' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='1024' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='1032' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='1040' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='1048' y='4' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='1056' y='4' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='1064' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='1072' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='1080' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='1088' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='1096' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='1104' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='1112' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='1120' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='1128' y='4' fill='currentColor' style='font-size:1em'>V</text>
<text text-anchor='middle' x='1136' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='1144' y='4' fill='currentColor' style='font-size:1em'>N</text>
</g>
</svg>
</div>
<p>iOS clients: Shadowrocket, Stash, FoXray<br>
Android clients: v2rayNG, Clash Meta, NekoBox</p>
<hr>
<h2 id="debugging-time-aka-why-the-heck-doesnt-it-work">Debugging Time (a.k.a. “Why the heck doesnt it work?!”)</h2>
<h3 id="symptom-520-unknown-error-cloudflare">Symptom: <strong>520 Unknown Error (Cloudflare)</strong></h3>
<ul>
<li>Likely cause: Nginx couldnt talk to Trojan.</li>
<li>Check Nginx error log:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>tail -n <span style="color:#ae81ff">50</span> /var/log/nginx/error.log
</span></span></code></pre></div></li>
<li>If you see <code>upstream sent no valid HTTP/1.0 header</code> → youre mixing HTTPS/HTTP between Nginx and Trojan.</li>
</ul>
<hr>
<h3 id="symptom-526-invalid-ssl-certificate">Symptom: <strong>526 Invalid SSL certificate</strong></h3>
<ul>
<li>Cloudflare → Nginx cert mismatch.</li>
<li>Run:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>openssl s_client -connect web.example.com:443 -servername web.example.com -showcerts
</span></span></code></pre></div></li>
<li>Make sure CN = <code>web.example.com</code>. If not, fix your cert.</li>
</ul>
<hr>
<h3 id="symptom-blank-page-on-panel">Symptom: <strong>Blank page on panel</strong></h3>
<ul>
<li>Check if the base path matches exactly (case-sensitive, slash-sensitive).</li>
<li>Watch for sneaky trailing spaces!</li>
<li>Test locally:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -s -D- http://127.0.0.1:46309/panel-bananas_42/
</span></span></code></pre></div></li>
</ul>
<hr>
<h3 id="symptom-client-wont-connect">Symptom: <strong>Client wont connect</strong></h3>
<ul>
<li>Run a WebSocket test:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -i -k -H <span style="color:#e6db74">&#34;Connection: Upgrade&#34;</span> -H <span style="color:#e6db74">&#34;Upgrade: websocket&#34;</span> https://web.example.com/stealth-path_abcd1234
</span></span></code></pre></div>Expect <code>101 Switching Protocols</code>. If not, check Nginx config.</li>
</ul>
<hr>
<h3 id="symptom-censor-still-blocks-you">Symptom: <strong>Censor still blocks you</strong></h3>
<ul>
<li>Did you expose another service (like SSH, Matrix, or Minecraft) on the same IP?<br>
→ Theyll find your origin IP. Use a second VPS or lock those ports down.</li>
<li>Did you use an obvious path like <code>/ws</code>?<br>
→ Use a random-looking one like <code>/cdn-assets-329df/</code>.</li>
</ul>
<hr>
<h2 id="pro-tips--fun-tricks">Pro Tips &amp; Fun Tricks</h2>
<ul>
<li>Serve a fake blog or portfolio at root so your domain looks legit.</li>
<li>Rotate WebSocket paths occasionally.</li>
<li>Use Cloudflare <strong>Page Rules</strong> to disable Rocket Loader &amp; Minify for your VPN domain.</li>
<li>Make friends with your Nginx error.log — it will roast you but it tells the truth.</li>
</ul>
<hr>
<h2 id="conclusion">Conclusion</h2>
<p>By putting Trojan behind Cloudflare, youve given your VPN a shiny new disguise:</p>
<ul>
<li>Looks like normal HTTPS.</li>
<li>Hides your origin IP.</li>
<li>Forces censors into a tough choice: block Cloudflare (and half the web) or let you pass.</li>
</ul>
<p>Congrats — youve built a VPN with both <strong>style</strong> and <strong>stealth</strong>. 🥷</p>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/bypass/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/bypass/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/bypass/"></head></html>

View file

@ -0,0 +1,19 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Cloudflare | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/cloudflare/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/cloudflare/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/cloudflare/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/cloudflare/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Cloudflare"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Cloudflare"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/>Tags</a></div><h1>Cloudflare
<a href=/tags/cloudflare/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><header class=entry-header><h2 class=entry-hint-parent>Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod</h2></header><div class=entry-content><p>TL;DR — The site is built once (Hugo project), published twice:
Onion: http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ via Tor, no TLS/HSTS, bound to 127.0.0.1:3301. Clearnet: https://blog.alipourimjourneys.ir behind Cloudflare, Lets Encrypt cert, Onion-Location header pointing to the onion mirror. 1) Tor hidden service (onion) basics I used Tors v3 onion services and mapped onion port 80 → my local web server on 127.0.0.1:3301.
Install & configure Tor (Debian/Ubuntu):
sudo apt update && sudo apt install -y tor sudoedit /etc/tor/torrc Add:
...</p></div><footer class=entry-footer><span title='2025-09-19 00:00:00 +0000 UTC'>September 19, 2025</span>&nbsp;·&nbsp;6 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/></a></article><article class="post-entry tag-entry"><header class=entry-header><h2 class=entry-hint-parent>Stealth Trojan VPN Behind Cloudflare Guide</h2></header><div class=entry-content><p>Introduction So you want a VPN that doesnt scream “I am a VPN” to every censor and firewall out there?
Welcome to the world of Trojan over WebSocket + TLS behind Cloudflare.
This guide not only shows you how to set it up but also sprinkles in some debugging magic so you can figure out why things break (and they will break, trust me).
Well anonymise domains and secrets, so substitute with your own:
...</p></div><footer class=entry-footer><span title='2025-09-09 11:05:00 +0200 +0200'>September 9, 2025</span>&nbsp;·&nbsp;4 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Stealth Trojan VPN Behind Cloudflare Guide" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,798 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Cloudflare on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/cloudflare/</link><description>Recent content in Cloudflare on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Fri, 19 Sep 2025 00:00:00 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/cloudflare/index.xml" rel="self" type="application/rss+xml"/><item><title>Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/</link><pubDate>Fri, 19 Sep 2025 00:00:00 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/</guid><description>How I hosted a Hugo + PaperMod site both as a Tor onion service and a normal HTTPS site behind Cloudflare, with clean configs, dual builds, and a one-command deploy.</description><content:encoded><![CDATA[<blockquote>
<p>TL;DR — The site is built once (Hugo project), <strong>published twice</strong>:</p>
<ul>
<li><strong>Onion</strong>: <code>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/</code> via Tor, no TLS/HSTS, bound to <code>127.0.0.1:3301</code>.</li>
<li><strong>Clearnet</strong>: <code>https://blog.alipourimjourneys.ir</code> behind Cloudflare, Lets Encrypt cert, <code>Onion-Location</code> header pointing to the onion mirror.</li>
</ul></blockquote>
<hr>
<h2 id="1-tor-hidden-service-onion-basics">1) Tor hidden service (onion) basics</h2>
<p>I used Tors v3 onion services and mapped onion port 80 → my local web server on <code>127.0.0.1:3301</code>.</p>
<p><strong>Install &amp; configure Tor (Debian/Ubuntu):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt update <span style="color:#f92672">&amp;&amp;</span> sudo apt install -y tor
</span></span><span style="display:flex;"><span>sudoedit /etc/tor/torrc
</span></span></code></pre></div><p>Add:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 344 41"
>
<g transform='translate(8,16)'>
<path d='M 132,8 L 124,40' fill='none' stroke='currentColor'></path>
<path d='M 196,8 L 188,40' fill='none' stroke='currentColor'></path>
<path d='M 228,8 L 220,40' fill='none' stroke='currentColor'></path>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>8</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>7</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>/</text>
</g>
</svg>
</div>
<p>Make sure the directory is owned by Tors user and private:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo mkdir -p /var/lib/tor/hidden_site
</span></span><span style="display:flex;"><span>sudo chown -R debian-tor:debian-tor /var/lib/tor/hidden_site
</span></span><span style="display:flex;"><span>sudo chmod <span style="color:#ae81ff">700</span> /var/lib/tor/hidden_site
</span></span></code></pre></div><p><strong>Important:</strong> use the right systemd unit:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># validate as the Tor user</span>
</span></span><span style="display:flex;"><span>sudo -u debian-tor tor -f /etc/tor/torrc --verify-config
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># (re)start the real service</span>
</span></span><span style="display:flex;"><span>sudo systemctl enable --now tor@default
</span></span><span style="display:flex;"><span>sudo systemctl restart tor@default
</span></span></code></pre></div><p>Get the onion address:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo cat /var/lib/tor/hidden_site/hostname
</span></span></code></pre></div><p>Quick check (do remote DNS via SOCKS):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -I --socks5-hostname 127.0.0.1:9050 <span style="color:#e6db74">&#34;http://</span><span style="color:#66d9ef">$(</span>sudo cat /var/lib/tor/hidden_site/hostname<span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span>
</span></span></code></pre></div><hr>
<h2 id="2-nginx-for-the-onion-localhost-only">2) Nginx for the onion (localhost-only)</h2>
<p>I keep the onion site strictly on localhost: <strong>no HTTPS, no redirects, no HSTS</strong>. Tor already provides e2e encryption and authenticity.</p>
<p><code>/etc/nginx/sites-available/onion-blog</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> 127.0.0.1:<span style="color:#ae81ff">3301</span> <span style="color:#e6db74">default_server</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">&lt;your-56-char&gt;.onion</span> 127.0.0.1 <span style="color:#e6db74">localhost</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># keep onion simple; no HSTS/redirects here
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Referrer-Policy</span> <span style="color:#e6db74">no-referrer</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">X-Content-Type-Options</span> <span style="color:#e6db74">nosniff</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">X-Frame-Options</span> <span style="color:#e6db74">SAMEORIGIN</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># (optional) strict CSP so nothing leaks to clearnet
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e"># add_header Content-Security-Policy &#34;default-src &#39;self&#39;; img-src &#39;self&#39; data:; style-src &#39;self&#39; &#39;unsafe-inline&#39;; script-src &#39;self&#39;&#34; always;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/srv/hugo/mysite/public-onion</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">index</span> <span style="color:#e6db74">index.html</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> { <span style="color:#f92672">try_files</span> $uri $uri/ <span style="color:#e6db74">/index.html</span>; }
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> ~<span style="color:#e6db74">*</span> <span style="color:#e6db74">\.(css|js|ico|png|jpg|jpeg|gif|svg|webp|txt|xml)</span>$ {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">access_log</span> <span style="color:#66d9ef">off</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Cache-Control</span> <span style="color:#e6db74">&#34;public,</span> <span style="color:#e6db74">max-age=31536000,</span> <span style="color:#e6db74">immutable&#34;</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">try_files</span> $uri =<span style="color:#ae81ff">404</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Enable/reload:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo ln -sf /etc/nginx/sites-available/onion-blog /etc/nginx/sites-enabled/onion-blog
</span></span><span style="display:flex;"><span>sudo nginx -t <span style="color:#f92672">&amp;&amp;</span> sudo systemctl reload nginx
</span></span></code></pre></div><blockquote>
<p>Gotcha I hit: I had <strong>two</strong> server blocks on <code>127.0.0.1:3301</code>, which caused 404s via onion. Make sure only the intended vhost listens there (or mark it <code>default_server</code>). Also, if you ever use a regex in <code>server_name</code>, the syntax is <code>server_name ~* \.onion$</code> (note the space after <code>~*</code>).</p></blockquote>
<hr>
<h2 id="3-hugo--papermod-setup-and-version-bumps">3) Hugo + PaperMod setup (and version bumps)</h2>
<p>PaperMod now requires <strong>Hugo Extended ≥ 0.146.0</strong>. I installed the extended binary from the official tarball to avoid Snaps sandbox limitations (Snap cant read <code>/srv</code> paths by default).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># install Hugo extended (example)</span>
</span></span><span style="display:flex;"><span>VER<span style="color:#f92672">=</span>0.146.0
</span></span><span style="display:flex;"><span>cd /tmp
</span></span><span style="display:flex;"><span>wget https://github.com/gohugoio/hugo/releases/download/v<span style="color:#e6db74">${</span>VER<span style="color:#e6db74">}</span>/hugo_extended_<span style="color:#e6db74">${</span>VER<span style="color:#e6db74">}</span>_Linux-amd64.tar.gz
</span></span><span style="display:flex;"><span>tar -xzf hugo_extended_<span style="color:#e6db74">${</span>VER<span style="color:#e6db74">}</span>_Linux-amd64.tar.gz
</span></span><span style="display:flex;"><span>sudo mv hugo /usr/local/bin/hugo
</span></span><span style="display:flex;"><span>hugo version <span style="color:#75715e"># should say &#34;extended&#34; and &gt;= 0.146.0</span>
</span></span></code></pre></div><p>Create the site and theme:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo mkdir -p /srv/hugo <span style="color:#f92672">&amp;&amp;</span> sudo chown -R <span style="color:#e6db74">&#34;</span>$USER<span style="color:#e6db74">&#34;</span>:<span style="color:#e6db74">&#34;</span>$USER<span style="color:#e6db74">&#34;</span> /srv/hugo
</span></span><span style="display:flex;"><span>cd /srv/hugo
</span></span><span style="display:flex;"><span>hugo new site mysite
</span></span><span style="display:flex;"><span>cd mysite
</span></span><span style="display:flex;"><span>git init
</span></span><span style="display:flex;"><span>git submodule add https://github.com/adityatelange/hugo-PaperMod themes/PaperMod
</span></span></code></pre></div><p><strong>Config updates:</strong> in newer Hugo, <code>paginate</code> is deprecated → use:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span><span style="color:#75715e"># config/_default/hugo.toml</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">title</span> = <span style="color:#e6db74">&#34;My Blog&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">theme</span> = <span style="color:#e6db74">&#34;PaperMod&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">enableRobotsTXT</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">pagination</span>]
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">pagerSize</span> = <span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">params</span>]
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">defaultTheme</span> = <span style="color:#e6db74">&#34;auto&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showReadingTime</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showPostNavLinks</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showBreadCrumbs</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showCodeCopyButtons</span> = <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><p>I added per-environment overrides so I can build two outputs with different <code>baseURL</code>s:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span><span style="color:#75715e"># config/clearnet/hugo.toml</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">baseURL</span> = <span style="color:#e6db74">&#34;https://blog.alipourimjourneys.ir/&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># config/onion/hugo.toml</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">baseURL</span> = <span style="color:#e6db74">&#34;http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/&#34;</span>
</span></span></code></pre></div><hr>
<h2 id="4-dual-builds-clearnet--onion-and-one-command-deploy">4) Dual builds (clearnet + onion) and one-command deploy</h2>
<p>I publish the same content twice—once for each base URL and docroot:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd /srv/hugo/mysite
</span></span><span style="display:flex;"><span><span style="color:#75715e"># clearnet build (served over HTTPS)</span>
</span></span><span style="display:flex;"><span>hugo --minify --environment clearnet -d public-clearnet
</span></span><span style="display:flex;"><span><span style="color:#75715e"># onion build (served via Tor)</span>
</span></span><span style="display:flex;"><span>hugo --minify --environment onion -d public-onion
</span></span><span style="display:flex;"><span>sudo systemctl reload nginx
</span></span></code></pre></div><p>Helper script I use:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo tee /usr/local/bin/build-both &gt;/dev/null <span style="color:#e6db74">&lt;&lt;&#39;EOF&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">set -euo pipefail
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">cd /srv/hugo/mysite
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">hugo --minify --environment clearnet -d public-clearnet
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">hugo --minify --environment onion -d public-onion
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">sudo systemctl reload nginx
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">echo &#34;Deployed both at $(date)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span>sudo chmod +x /usr/local/bin/build-both
</span></span></code></pre></div><p>While editing, I sometimes auto-rebuild on file save:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt install -y entr
</span></span><span style="display:flex;"><span>cd /srv/hugo/mysite
</span></span><span style="display:flex;"><span>find content layouts assets static config -type f | entr -r build-both
</span></span></code></pre></div><hr>
<h2 id="5-clearnet-behind-cloudflare--lets-encrypt-manual-dns-01">5) Clearnet behind Cloudflare + Lets Encrypt (manual DNS-01)</h2>
<p>Cloudflare is set to <strong>Full (strict)</strong>. I issued a public cert for <code>blog.alipourimjourneys.ir</code> using <strong>manual DNS-01</strong> (no API token):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo snap install --classic certbot
</span></span><span style="display:flex;"><span>sudo certbot certonly --manual --preferred-challenges dns -d blog.alipourimjourneys.ir --agree-tos -m you@example.com --no-eff-email
</span></span></code></pre></div><p>Certbot tells you to add a TXT record <code>_acme-challenge.blog.alipourimjourneys.ir</code>. Add it in Cloudflare DNS, verify with <code>dig</code>, then continue. Cert ends up at:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 496 41"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='280' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='288' y='20' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='296' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='304' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='312' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='320' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='336' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='344' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='352' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='360' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='368' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='376' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='384' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='392' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='400' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='408' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='416' y='20' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='424' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='432' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='440' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='448' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='456' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='464' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>m</text>
</g>
</svg>
</div>
<p>Clearnet Nginx vhosts:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#75715e"># HTTP → HTTPS redirect (optional; CF usually talks HTTPS to origin anyway)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">80</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:80</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">blog.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">return</span> <span style="color:#ae81ff">301</span> <span style="color:#e6db74">https://blog.alipourimjourneys.ir</span>$request_uri;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># HTTPS origin (behind Cloudflare)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">blog.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/blog.alipourimjourneys.ir/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/blog.alipourimjourneys.ir/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># HSTS on clearnet only
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Strict-Transport-Security</span> <span style="color:#e6db74">&#34;max-age=31536000</span>; <span style="color:#f92672">includeSubDomains</span>; <span style="color:#f92672">preload&#34;</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Help Tor Browser discover the onion mirror (safe on clearnet)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Onion-Location</span> <span style="color:#e6db74">&#34;http://&lt;your-56-char&gt;.onion</span>$request_uri&#34; <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/srv/hugo/mysite/public-clearnet</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">index</span> <span style="color:#e6db74">index.html</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> { <span style="color:#f92672">try_files</span> $uri $uri/ <span style="color:#e6db74">/index.html</span>; }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>Note: dont add HSTS or HTTPS redirects to the <strong>onion</strong> vhost. Keep onion pure HTTP on localhost.</p></blockquote>
<hr>
<h2 id="6-troubleshooting-i-ran-into-and-fixes">6) Troubleshooting I ran into (and fixes)</h2>
<ul>
<li><strong>Tor unit confusion:</strong> <code>tor.service</code> is a tiny master; the real daemon is <code>tor@default</code>. Use that unit and verify config as <code>debian-tor</code>.</li>
<li><strong>Permissions:</strong> <code>HiddenServiceDir</code> must be owned by <code>debian-tor</code> and mode <code>700</code>.</li>
<li><strong>Mapping mismatch:</strong> If <code>HiddenServicePort 80 127.0.0.1:3301</code> is set, visit <code>http://&lt;onion&gt;/</code> (no <code>:3301</code>). If you set <code>HiddenServicePort 3301 127.0.0.1:3301</code>, you must use <code>http://&lt;onion&gt;:3301/</code>.</li>
<li><strong>Curl &amp; .onion:</strong> modern curl refuses <code>.onion</code> unless you use remote DNS via SOCKS:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -I --socks5-hostname 127.0.0.1:9050 <span style="color:#e6db74">&#34;http://&lt;onion&gt;/&#34;</span>
</span></span></code></pre></div></li>
<li><strong>Two Nginx vhosts on the same port:</strong> I had a duplicate server on <code>127.0.0.1:3301</code> pointing somewhere else, which caused onion 404s. Keep only one (or mark one <code>default_server</code>).</li>
<li><strong>Regex in <code>server_name</code>:</strong> if you use it, write <code>server_name ~* \.onion$</code> (space after <code>~*</code>). I fixed an <code>invalid variable name</code> error caused by a missing space.</li>
<li><strong>PaperMod with old Hugo:</strong> upgraded to <strong>extended ≥ 0.146.0</strong>. Also updated <code>paginate</code><code>[pagination].pagerSize</code>.</li>
<li><strong>Snap confinement:</strong> Snaps <code>hugo</code> couldnt read <code>/srv</code> (<code>.../void: permission denied</code>). Switched to the tarball build in <code>/usr/local/bin</code>.</li>
</ul>
<hr>
<h2 id="7-not-secure-in-tor-browser">7) “Not secure” in Tor Browser?</h2>
<p>That message can appear because onion uses <strong>HTTP</strong>. Its OK: Tor provides e2e encryption + onion auth. If you enable HTTPS-Only Mode, add an exception for the site. Also ensure the onion build doesnt reference <strong>clearnet</strong> resources (scan the built HTML for <code>http(s)://</code> links that arent your onion).</p>
<hr>
<h2 id="8-day-to-day-workflow">8) Day-to-day workflow</h2>
<ul>
<li>Create content:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>hugo new posts/my-first-post.md <span style="color:#75715e"># then set draft: false</span>
</span></span></code></pre></div></li>
<li>Publish both:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>build-both
</span></span></code></pre></div></li>
<li>(Optional) Auto on save:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>find content layouts assets static config -type f | entr -r build-both
</span></span></code></pre></div></li>
<li>(Optional) Git push-to-deploy with a bare repo + post-receive hook that runs <code>build-both</code>.</li>
</ul>
<hr>
<h2 id="final-notes">Final notes</h2>
<ul>
<li>Clearnet gets <strong>HTTPS + HSTS</strong> and an <code>Onion-Location</code> header.</li>
<li>Onion gets <strong>no HSTS/redirects</strong>, and all assets are self-hosted to avoid mixed content.</li>
<li>Serving both worlds from one Hugo repo is easy: <strong>two builds, two vhosts, one workflow</strong>.</li>
</ul>
]]></content:encoded></item><item><title>Stealth Trojan VPN Behind Cloudflare Guide</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/</link><pubDate>Tue, 09 Sep 2025 11:05:00 +0200</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>So you want a VPN that &lt;strong>doesn&amp;rsquo;t scream &amp;ldquo;I am a VPN&amp;rdquo;&lt;/strong> to every censor and firewall out there?&lt;br>
Welcome to the world of &lt;strong>Trojan over WebSocket + TLS behind Cloudflare&lt;/strong>.&lt;/p>
&lt;p>This guide not only shows you how to set it up but also sprinkles in some &lt;strong>debugging magic&lt;/strong> so you can figure out why things break (and they &lt;em>will&lt;/em> break, trust me).&lt;/p>
&lt;p>Well anonymise domains and secrets, so substitute with your own:&lt;/p></description><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>So you want a VPN that <strong>doesn&rsquo;t scream &ldquo;I am a VPN&rdquo;</strong> to every censor and firewall out there?<br>
Welcome to the world of <strong>Trojan over WebSocket + TLS behind Cloudflare</strong>.</p>
<p>This guide not only shows you how to set it up but also sprinkles in some <strong>debugging magic</strong> so you can figure out why things break (and they <em>will</em> break, trust me).</p>
<p>Well anonymise domains and secrets, so substitute with your own:</p>
<ul>
<li>VPN domain: <code>web.example.com</code></li>
<li>Panel domain: <code>panel.example.com</code></li>
<li>Secret WS path: <code>/stealth-path_abcd1234</code></li>
<li>Password: <code>&lt;PASSWORD&gt;</code></li>
</ul>
<hr>
<h2 id="architecture-at-a-glance">Architecture at a Glance</h2>
<p>Think of it as a disguise party:</p>
<ul>
<li><strong>Trojan</strong> = the shy guest (your VPN protocol)</li>
<li><strong>Nginx</strong> = the bouncer checking IDs (reverse proxy)</li>
<li><strong>Cloudflare</strong> = the doorman who makes sure nobody sees who&rsquo;s inside (CDN &amp; proxy)</li>
<li><strong>Your fake website</strong> = the mask (camouflage page)</li>
</ul>
<p>Traffic flow:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 536 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'></text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'></text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>N</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'></text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>5</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='504' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='512' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='520' y='4' fill='currentColor' style='font-size:1em'>)</text>
</g>
</svg>
</div>
<hr>
<h2 id="step-1-panel-setup-panelexamplecom">Step 1: Panel Setup (<code>panel.example.com</code>)</h2>
<ul>
<li>Bind the 3x-ui panel to localhost (e.g., <code>127.0.0.1:46309</code>).</li>
<li>Choose a funky <strong>web base path</strong> like <code>/panel-bananas_42/</code>.</li>
<li>Proxy it through Nginx with HTTPS + Basic Auth.</li>
<li>Test it at <code>https://panel.example.com/panel-bananas_42/</code>.</li>
</ul>
<p><strong>Pro tip:</strong> If you see a blank page → your base path is mismatched or Nginx is eating it. Check logs!</p>
<hr>
<h2 id="step-2-trojan-inbound">Step 2: Trojan Inbound</h2>
<p>In 3x-ui, create a Trojan inbound:</p>
<ul>
<li>Local port: <code>54321</code></li>
<li>Transport: WebSocket</li>
<li>Path: <code>/stealth-path_abcd1234</code></li>
<li>Security: none</li>
<li>Password: <code>&lt;PASSWORD&gt;</code></li>
</ul>
<hr>
<h2 id="step-3-nginx-for-vpn-domain-webexamplecom">Step 3: Nginx for VPN Domain (<code>web.example.com</code>)</h2>
<p>Your Nginx is the gatekeeper. Sample config:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">web.example.com</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/web.example.com/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/web.example.com/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Fake website at root
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/var/www/html</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">index</span> <span style="color:#e6db74">index.html</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Real VPN under secret WS path
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/stealth-path_abcd1234</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:54321</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_http_version</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">.1</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">&#34;upgrade&#34;</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><hr>
<h2 id="step-4-firewall-rules">Step 4: Firewall Rules</h2>
<p>Lock things down! Only Cloudflare should reach you:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 22/tcp
</span></span><span style="display:flex;"><span>ufw allow 80/tcp
</span></span><span style="display:flex;"><span>ufw allow 443/tcp
</span></span><span style="display:flex;"><span>ufw deny 54321/tcp
</span></span></code></pre></div><hr>
<h2 id="step-5-client-config">Step 5: Client Config</h2>
<p>Trojan URI:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 1160 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>&lt;</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>W</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>&gt;</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>@</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>?</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='504' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='512' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='520' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='528' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='536' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='544' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='552' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='560' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='568' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='576' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='584' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='592' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='600' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='608' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='616' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='624' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='632' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='640' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='648' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='656' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='664' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='672' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='680' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='688' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='696' y='4' fill='currentColor' style='font-size:1em'>%</text>
<text text-anchor='middle' x='704' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='712' y='4' fill='currentColor' style='font-size:1em'>F</text>
<text text-anchor='middle' x='720' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='728' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='736' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='744' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='752' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='760' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='768' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='776' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='784' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='792' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='800' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='808' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='816' y='4' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='824' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='832' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='840' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='848' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='856' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='864' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='872' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='880' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='888' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='896' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='904' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='912' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='920' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='928' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='936' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='944' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='952' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='960' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='968' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='976' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='984' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='992' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='1000' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='1008' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='1016' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='1024' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='1032' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='1040' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='1048' y='4' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='1056' y='4' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='1064' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='1072' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='1080' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='1088' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='1096' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='1104' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='1112' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='1120' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='1128' y='4' fill='currentColor' style='font-size:1em'>V</text>
<text text-anchor='middle' x='1136' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='1144' y='4' fill='currentColor' style='font-size:1em'>N</text>
</g>
</svg>
</div>
<p>iOS clients: Shadowrocket, Stash, FoXray<br>
Android clients: v2rayNG, Clash Meta, NekoBox</p>
<hr>
<h2 id="debugging-time-aka-why-the-heck-doesnt-it-work">Debugging Time (a.k.a. “Why the heck doesnt it work?!”)</h2>
<h3 id="symptom-520-unknown-error-cloudflare">Symptom: <strong>520 Unknown Error (Cloudflare)</strong></h3>
<ul>
<li>Likely cause: Nginx couldnt talk to Trojan.</li>
<li>Check Nginx error log:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>tail -n <span style="color:#ae81ff">50</span> /var/log/nginx/error.log
</span></span></code></pre></div></li>
<li>If you see <code>upstream sent no valid HTTP/1.0 header</code> → youre mixing HTTPS/HTTP between Nginx and Trojan.</li>
</ul>
<hr>
<h3 id="symptom-526-invalid-ssl-certificate">Symptom: <strong>526 Invalid SSL certificate</strong></h3>
<ul>
<li>Cloudflare → Nginx cert mismatch.</li>
<li>Run:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>openssl s_client -connect web.example.com:443 -servername web.example.com -showcerts
</span></span></code></pre></div></li>
<li>Make sure CN = <code>web.example.com</code>. If not, fix your cert.</li>
</ul>
<hr>
<h3 id="symptom-blank-page-on-panel">Symptom: <strong>Blank page on panel</strong></h3>
<ul>
<li>Check if the base path matches exactly (case-sensitive, slash-sensitive).</li>
<li>Watch for sneaky trailing spaces!</li>
<li>Test locally:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -s -D- http://127.0.0.1:46309/panel-bananas_42/
</span></span></code></pre></div></li>
</ul>
<hr>
<h3 id="symptom-client-wont-connect">Symptom: <strong>Client wont connect</strong></h3>
<ul>
<li>Run a WebSocket test:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -i -k -H <span style="color:#e6db74">&#34;Connection: Upgrade&#34;</span> -H <span style="color:#e6db74">&#34;Upgrade: websocket&#34;</span> https://web.example.com/stealth-path_abcd1234
</span></span></code></pre></div>Expect <code>101 Switching Protocols</code>. If not, check Nginx config.</li>
</ul>
<hr>
<h3 id="symptom-censor-still-blocks-you">Symptom: <strong>Censor still blocks you</strong></h3>
<ul>
<li>Did you expose another service (like SSH, Matrix, or Minecraft) on the same IP?<br>
→ Theyll find your origin IP. Use a second VPS or lock those ports down.</li>
<li>Did you use an obvious path like <code>/ws</code>?<br>
→ Use a random-looking one like <code>/cdn-assets-329df/</code>.</li>
</ul>
<hr>
<h2 id="pro-tips--fun-tricks">Pro Tips &amp; Fun Tricks</h2>
<ul>
<li>Serve a fake blog or portfolio at root so your domain looks legit.</li>
<li>Rotate WebSocket paths occasionally.</li>
<li>Use Cloudflare <strong>Page Rules</strong> to disable Rocket Loader &amp; Minify for your VPN domain.</li>
<li>Make friends with your Nginx error.log — it will roast you but it tells the truth.</li>
</ul>
<hr>
<h2 id="conclusion">Conclusion</h2>
<p>By putting Trojan behind Cloudflare, youve given your VPN a shiny new disguise:</p>
<ul>
<li>Looks like normal HTTPS.</li>
<li>Hides your origin IP.</li>
<li>Forces censors into a tough choice: block Cloudflare (and half the web) or let you pass.</li>
</ul>
<p>Congrats — youve built a VPN with both <strong>style</strong> and <strong>stealth</strong>. 🥷</p>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/cloudflare/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/cloudflare/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/cloudflare/"></head></html>

View file

@ -0,0 +1,12 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Coturn | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/coturn/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/coturn/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/coturn/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/coturn/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Coturn"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Coturn"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/>Tags</a></div><h1>Coturn
<a href=/tags/coturn/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><figure class=entry-cover><img loading=lazy src=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/images/matrix-cover.png alt="Matrix, Signal but distributed"></figure><header class=entry-header><h2 class=entry-hint-parent>Self-hosting Matrix + Element Call with LiveKit: from zero to working (and the [not so!!] fun debugging along the way)</h2></header><div class=entry-content><p>TL;DR: The call kept saying “waiting for media” because the browser never opened a WebSocket to LiveKit. The root cause was duplicate Access-Control-Allow-Origin headers on /sfu/get (CORS), which stopped the JWT response. Fixing CORS and ensuring the WS proxy worked (HTTP 101 in logs) solved it.
What Im building A Matrix homeserver (Synapse) at matrix.example.com (replace with your domain). TURN/STUN (coTURN) for NAT traversal. Element Call backed by LiveKit, fronted by rtc.example.com. Nginx (host) as the single reverse proxy for everything. Cloudflare DNS (with rtc.* set to DNS-only, no orange cloud). UFW firewall opened for Matrix federation, TURN, and LiveKit media ports. I used Docker for Synapse, PostgreSQL, LiveKit and the JWT helper. I used host Nginx (not Nginx in Docker) to avoid port binding conflicts on 80/443/8448.
...</p></div><footer class=entry-footer><span title='2025-08-26 00:00:00 +0000 UTC'>August 26, 2025</span>&nbsp;·&nbsp;9 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/matrix_setup/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Self-hosting Matrix + Element Call with LiveKit: from zero to working (and the [not so!!] fun debugging along the way)" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/matrix_setup/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,639 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Coturn on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/coturn/</link><description>Recent content in Coturn on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Tue, 26 Aug 2025 00:00:00 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/coturn/index.xml" rel="self" type="application/rss+xml"/><item><title>Self-hosting Matrix + Element Call with LiveKit: from zero to working (and the [not so!!] fun debugging along the way)</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/matrix_setup/</link><pubDate>Tue, 26 Aug 2025 00:00:00 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/matrix_setup/</guid><description>How I set up a Matrix homeserver (Synapse) with TURN and Element Call using LiveKit, plus the exact debugging steps that took it from &amp;#39;waiting for media&amp;#39; to solid calls.</description><content:encoded><![CDATA[<blockquote>
<p>TL;DR: The call kept saying <strong>“waiting for media”</strong> because the browser never opened a WebSocket to LiveKit. The root cause was <strong>duplicate <code>Access-Control-Allow-Origin</code> headers</strong> on <code>/sfu/get</code> (CORS), which stopped the JWT response. Fixing CORS and ensuring the WS proxy worked (HTTP <strong>101</strong> in logs) solved it.</p></blockquote>
<h2 id="what-im-building">What I&rsquo;m building</h2>
<ul>
<li>A <strong>Matrix homeserver</strong> (Synapse) at <code>matrix.example.com</code> (replace with your domain).</li>
<li><strong>TURN/STUN</strong> (coTURN) for NAT traversal.</li>
<li><strong>Element Call</strong> backed by <strong>LiveKit</strong>, fronted by <code>rtc.example.com</code>.</li>
<li><strong>Nginx (host)</strong> as the single reverse proxy for everything.</li>
<li><strong>Cloudflare</strong> DNS (with <code>rtc.*</code> set to <strong>DNS-only</strong>, no orange cloud).</li>
<li><strong>UFW</strong> firewall opened for Matrix federation, TURN, and LiveKit media ports.</li>
</ul>
<blockquote>
<p>I used Docker for Synapse, PostgreSQL, LiveKit and the JWT helper. I used <strong>host</strong> Nginx (not Nginx in Docker) to avoid port binding conflicts on 80/443/8448.</p></blockquote>
<hr>
<h2 id="prereqs">Prereqs</h2>
<ul>
<li>DNS A/AAAA:
<ul>
<li><code>matrix.example.com</code> → your server (v4/v6)</li>
<li><code>rtc.example.com</code> → your server (v4/v6)</li>
</ul>
</li>
<li>Certificates:
<ul>
<li><code>matrix.example.com</code> and <code>rtc.example.com</code> via Lets Encrypt on the host</li>
</ul>
</li>
<li>Cloudflare: <strong>DNS-only (grey cloud)</strong> for <code>rtc.example.com</code> so WebSockets &amp; UDP work without interference.</li>
<li>UFW / firewall open:
<ul>
<li>80/tcp, 443/tcp</li>
<li>8448/tcp (Matrix federation)</li>
<li>3478/tcp, 3478/udp and <strong>5349/tcp</strong> (TURN/TLS)</li>
<li><strong>LiveKit</strong>: 7881/tcp and <strong>5010050200/udp</strong> (or your chosen range)</li>
</ul>
</li>
<li>Docker + docker compose installed.</li>
</ul>
<hr>
<h2 id="synapse--postgresql">Synapse + PostgreSQL</h2>
<h3 id="1-the-postgresql-collation-gotcha">1) The PostgreSQL collation gotcha</h3>
<p>Synapse prefers the database collation <strong><code>C</code></strong>. If your Postgres cluster was initialized with <code>en_US.utf8</code>, Synapse will error like:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 512 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>'</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>U</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>8</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>'</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>'</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>'</text>
</g>
</svg>
</div>
<p><strong>Two ways to resolve:</strong></p>
<ul>
<li><strong>Preferred (clean)</strong>: Re-initialize the Postgres <strong>cluster</strong> with <code>C</code> and <code>UTF-8</code>:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># docker-compose.yml (excerpt for Postgres)</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">db</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">postgres:16</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_DB</span>: <span style="color:#ae81ff">synapse</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_USER</span>: <span style="color:#ae81ff">synapse</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_PASSWORD</span>: <span style="color:#ae81ff">&lt;strong-password&gt;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_INITDB_ARGS</span>: <span style="color:#e6db74">&#34;--locale=C --encoding=UTF8 --lc-collate=C --lc-ctype=C&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">./pgdata:/var/lib/postgresql/data</span>
</span></span></code></pre></div><blockquote>
<p>Requires wiping the volume and recreating the DB.</p></blockquote>
<ul>
<li><strong>Pragmatic (works quickly)</strong>: In Synapses DB config, set <code>allow_unsafe_locale: true</code>. This bypasses the check. Its fine for hobby use; for production, prefer the clean <code>C</code> cluster.</li>
</ul>
<h3 id="2-start-synapse-and-generate-config">2) Start Synapse and generate config</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose up -d db synapse
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Logs</span>
</span></span><span style="display:flex;"><span>docker compose logs --tail<span style="color:#f92672">=</span><span style="color:#ae81ff">200</span> synapse
</span></span></code></pre></div><p>Ensure Synapse prints your <strong>server_name</strong> and <strong>public base URL</strong> and stays up.</p>
<h3 id="3-create-an-admin-user">3) Create an admin user</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Exec into the running Synapse container:</span>
</span></span><span style="display:flex;"><span>docker compose exec synapse register_new_matrix_user <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -c /data/homeserver.yaml -u &lt;username&gt; -p &lt;password&gt; <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -a -k
</span></span></code></pre></div><blockquote>
<p>If you see “Unknown execution mode”, you probably ran the binary with the wrong entrypoint. Use <code>docker compose exec synapse …</code> against the running container.</p></blockquote>
<hr>
<h2 id="turn-coturn">TURN (coTURN)</h2>
<h3 id="1-avoid-bad-inline-comments">1) Avoid bad inline comments</h3>
<p>If you see errors like:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 808 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>U</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>Y</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='512' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='520' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='528' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='544' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='552' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='560' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='568' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='576' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='584' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='592' y='4' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='608' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='616' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='624' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='632' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='640' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='648' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='656' y='4' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='672' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='680' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='688' y='4' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='696' y='4' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='712' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='720' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='728' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='736' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='744' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='752' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='760' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='768' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='776' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='784' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='792' y='4' fill='currentColor' style='font-size:1em'>.</text>
</g>
</svg>
</div>
<p>…it means a <code>#</code> comment is on the <strong>same line</strong> as a boolean directive. Move comments to their own lines.</p>
<h3 id="2-minimal-turnserverconf">2) Minimal <code>turnserver.conf</code></h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#a6e22e">listening-port</span><span style="color:#f92672">=</span><span style="color:#e6db74">3478</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">tls-listening-port</span><span style="color:#f92672">=</span><span style="color:#e6db74">5349</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">fingerprint</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">use-auth-secret</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">static-auth-secret</span><span style="color:#f92672">=</span><span style="color:#e6db74">&lt;shared-secret&gt; # also set in Synapse</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">realm</span><span style="color:#f92672">=</span><span style="color:#e6db74">example.com # used in creds generation</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">total-quota</span><span style="color:#f92672">=</span><span style="color:#e6db74">0</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">bps-capacity</span><span style="color:#f92672">=</span><span style="color:#e6db74">0</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">cli-password</span><span style="color:#f92672">=</span><span style="color:#e6db74">&lt;admin-pass&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">no-cli</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">cert</span><span style="color:#f92672">=</span><span style="color:#e6db74">/etc/letsencrypt/live/turn.example.com/fullchain.pem</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">pkey</span><span style="color:#f92672">=</span><span style="color:#e6db74">/etc/letsencrypt/live/turn.example.com/privkey.pem</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># If behind NAT:</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># external-ip=&lt;public-ip&gt;/&lt;internal-ip&gt;</span>
</span></span></code></pre></div><h3 id="3-wire-turn-into-synapse">3) Wire TURN into Synapse</h3>
<p>In <code>homeserver.yaml</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">turn_uris</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;turn:turn.example.com?transport=udp&#34;</span>
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;turn:turn.example.com?transport=tcp&#34;</span>
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;turns:turn.example.com:5349?transport=tcp&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">turn_shared_secret</span>: <span style="color:#e6db74">&#34;&lt;shared-secret&gt;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">turn_user_lifetime</span>: <span style="color:#e6db74">&#34;1d&#34;</span>
</span></span></code></pre></div><p>Verify the homeserver issues TURN creds:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>TOKEN<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;&lt;your matrix access token&gt;&#39;</span>
</span></span><span style="display:flex;"><span>curl -s -H <span style="color:#e6db74">&#34;Authorization: Bearer </span>$TOKEN<span style="color:#e6db74">&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> https://matrix.example.com/_matrix/client/v3/voip/turnServer | jq
</span></span></code></pre></div><p>You should see <code>uris</code>, a time-limited <code>username</code> and <code>password</code>.</p>
<hr>
<h2 id="nginx-host-for-synapse-and-federation">Nginx (host) for Synapse and federation</h2>
<p>Create <code>/etc/nginx/conf.d/matrix.conf</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#75715e"># Client traffic on 443
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:443</span> <span style="color:#e6db74">ssl</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">matrix.example.com</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/matrix.example.com/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/matrix.example.com/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Proxy client &amp; admin APIs to Synapse (container) on 8008
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> ~ <span style="color:#e6db74">^(/_matrix|/_synapse/client)</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:8008</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $remote_addr;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">client_max_body_size</span> <span style="color:#e6db74">50M</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Advertise homeserver base + RTC focus (MSC4143) via .well-known
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> = <span style="color:#e6db74">/.well-known/matrix/client</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">default_type</span> <span style="color:#e6db74">application/json</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Access-Control-Allow-Origin</span> <span style="color:#e6db74">&#34;*&#34;</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">return</span> <span style="color:#ae81ff">200</span> <span style="color:#e6db74">&#39;</span>{<span style="color:#f92672">&#34;m.homeserver&#34;:{&#34;base_url&#34;:&#34;https://matrix.example.com&#34;},&#34;org.matrix.msc4143.rtc_foci&#34;:[{&#34;type&#34;:&#34;livekit&#34;,&#34;livekit_service_url&#34;:&#34;https://rtc.example.com&#34;}]}&#39;</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Federation on 8448
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#f92672">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">8448</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:8448</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">matrix.example.com</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/matrix.example.com/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/matrix.example.com/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:8008</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $remote_addr;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">client_max_body_size</span> <span style="color:#e6db74">50M</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Reload and sanity check:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo nginx -t <span style="color:#f92672">&amp;&amp;</span> sudo systemctl reload nginx
</span></span><span style="display:flex;"><span>curl -s https://matrix.example.com/.well-known/matrix/client | jq
</span></span></code></pre></div><p>Also check Synapse supports RTC signaling (MSC4140) so clients actually use it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -s https://matrix.example.com/_matrix/client/versions | jq <span style="color:#e6db74">&#39;.unstable_features.&#34;org.matrix.msc4140&#34;&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># expect: true</span>
</span></span></code></pre></div><hr>
<h2 id="element-call--livekit">Element Call + LiveKit</h2>
<p>Ill run <strong>LiveKit</strong> and the small <strong>JWT helper</strong> (Elements <code>elementcall_jwt</code>) in Docker. LiveKit handles media; the JWT helper mints access tokens for WebSocket connects.</p>
<h3 id="1-livekit-config-etclivekityaml-inside-container">1) LiveKit config (<code>/etc/livekit.yaml</code> inside container)</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">port</span>: <span style="color:#ae81ff">7880</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">bind_addresses</span>: [<span style="color:#e6db74">&#34;0.0.0.0&#34;</span>]
</span></span><span style="display:flex;"><span><span style="color:#f92672">rtc</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">tcp_port</span>: <span style="color:#ae81ff">7881</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">port_range_start</span>: <span style="color:#ae81ff">50100</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">port_range_end</span>: <span style="color:#ae81ff">50200</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">use_external_ip</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">logging</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">level</span>: <span style="color:#ae81ff">info</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">turn</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">enabled</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">keys</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">lk_prod_1</span>: <span style="color:#e6db74">&#34;REPLACE_WITH_A_64_CHAR_RANDOM_SECRET___________________________________&#34;</span>
</span></span></code></pre></div><blockquote>
<p><strong>Important:</strong> the secret must be <strong>&gt;= 32 chars</strong>. The default <code>devkey</code> will trigger <code>secret is too short</code> warnings and wont work with the JWT helper.</p></blockquote>
<h3 id="2-elementcall_jwt-env">2) elementcall_jwt env</h3>
<p>Run it with:</p>
<ul>
<li><code>LIVEKIT_URL=wss://rtc.example.com</code> <em>(root WS URL, <strong>no</strong> <code>/livekit/sfu</code> path)</em></li>
<li><code>LIVEKIT_KEY=lk_prod_1</code></li>
<li><code>LIVEKIT_SECRET=&lt;the long secret above&gt;</code></li>
<li><code>LIVEKIT_JWT_PORT=8080</code> (internal HTTP port the proxy will hit)</li>
<li>Optionally: <code>LIVEKIT_FULL_ACCESS_HOMESERVERS=*</code> during setup</li>
</ul>
<p>Check logs on start; it prints the LIVEKIT_URL it will advertise.</p>
<h3 id="3-nginx-host-for-rtcexamplecom">3) Nginx (host) for <code>rtc.example.com</code></h3>
<p>Create <code>/etc/nginx/conf.d/rtc.conf</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:443</span> <span style="color:#e6db74">ssl</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">rtc.example.com</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/rtc.example.com/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/rtc.example.com/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">access_log</span> <span style="color:#e6db74">/var/log/nginx/rtc.access.log</span> <span style="color:#e6db74">combined</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># 3a) JWT endpoint with clean CORS (avoid duplicate ACAO)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> = <span style="color:#e6db74">/sfu/get</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_hide_header</span> <span style="color:#e6db74">Access-Control-Allow-Origin</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Access-Control-Allow-Origin</span> $http_origin <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Vary</span> <span style="color:#e6db74">&#34;Origin&#34;</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Access-Control-Allow-Methods</span> <span style="color:#e6db74">&#34;POST,</span> <span style="color:#e6db74">OPTIONS&#34;</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Access-Control-Allow-Headers</span> <span style="color:#e6db74">&#34;Accept,</span> <span style="color:#e6db74">Content-Type,</span> <span style="color:#e6db74">Content-Length,</span> <span style="color:#e6db74">Accept-Encoding,</span> <span style="color:#e6db74">X-CSRF-Token,</span> <span style="color:#e6db74">Authorization&#34;</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">if</span> <span style="color:#e6db74">(</span>$request_method = <span style="color:#e6db74">OPTIONS)</span> { <span style="color:#f92672">return</span> <span style="color:#ae81ff">204</span>; }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:8070/sfu/get</span>; <span style="color:#75715e"># elementcall_jwt
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># 3b) LiveKit WS &amp; HTTP (catch-all)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_http_version</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">.1</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">&#34;upgrade&#34;</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Sec-WebSocket-Protocol</span> $http_sec_websocket_protocol; <span style="color:#75715e"># &#34;livekit&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Origin</span> $http_origin;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_buffering</span> <span style="color:#66d9ef">off</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_read_timeout</span> <span style="color:#e6db74">3600s</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:7880</span>; <span style="color:#75715e"># livekit
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Reload and basic checks:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo nginx -t <span style="color:#f92672">&amp;&amp;</span> sudo systemctl reload nginx
</span></span><span style="display:flex;"><span><span style="color:#75715e"># JWT preflight (should return a single ACAO header)</span>
</span></span><span style="display:flex;"><span>curl -si -X OPTIONS https://rtc.example.com/sfu/get <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Origin: https://app.element.io&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Access-Control-Request-Method: POST&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Access-Control-Request-Headers: authorization, content-type&#39;</span> | sed -n <span style="color:#e6db74">&#39;1,30p&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Expected: one Access-Control-Allow-Origin and 200/204</span>
</span></span></code></pre></div><blockquote>
<p><strong>Why I did this:</strong> I initially had <strong>two</strong> <code>Access-Control-Allow-Origin</code> headers (one added by the upstream, one by Nginx). Browsers reject that with “Access-Control-Allow-Origin cannot contain more than one origin”, so the JWT response never reached the client. Fixing CORS fixed everything.</p></blockquote>
<hr>
<h2 id="the-waiting-for-media-debugging-story-how-i-found-it">The “waiting for media” debugging story (how I found it)</h2>
<p>Symptom: Element Call created rooms, but calls stayed on <strong>“waiting for media.”</strong></p>
<p>What worked:</p>
<ul>
<li><code>elementcall_jwt</code> could <strong>CreateRoom</strong> in LiveKit (seen in logs).</li>
<li>TURN creds endpoint returned time-limited credentials.</li>
</ul>
<p>What didnt appear:</p>
<ul>
<li>No <strong>HTTP 101</strong> lines in <code>rtc.access.log</code> → the <strong>browser never established a WebSocket</strong> to LiveKit.</li>
</ul>
<h3 id="step-1-prove-the-ws-vhost-works-even-without-auth">Step 1: prove the WS vhost works (even without auth)</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -v --http1.1 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Connection: Upgrade&#39;</span> -H <span style="color:#e6db74">&#39;Upgrade: websocket&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Sec-WebSocket-Version: 13&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> https://rtc.example.com/rtc -o /dev/null
</span></span></code></pre></div><p>Result: I got a <code>401</code> (expected), but importantly I saw a log line in <code>rtc.access.log</code>. So <strong>Nginx WS proxying was fine</strong>.</p>
<h3 id="step-2-check-the-browser-console">Step 2: check the browser console</h3>
<p>The smoking gun in DevTools:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 672 41"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>F</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>I</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='160' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='280' y='20' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='288' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='296' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='304' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='312' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='320' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='328' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='336' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='344' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='352' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='360' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='368' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='376' y='20' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='384' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='392' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='400' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='408' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='416' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='432' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='440' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='448' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='464' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='472' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='488' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='496' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='504' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='504' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='512' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='520' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='528' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='544' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='552' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='560' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='568' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='576' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='584' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='592' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='608' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='616' y='20' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='624' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='632' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='640' y='20' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='648' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='656' y='20' fill='currentColor' style='font-size:1em'>.</text>
</g>
</svg>
</div>
<p>The browser refused the JWT call due to duplicated <strong>ACAO</strong> headers, so no token → no WebSocket connect.</p>
<p><strong>Fix:</strong> in Nginx I added:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">proxy_hide_header</span> <span style="color:#e6db74">Access-Control-Allow-Origin</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">add_header</span> <span style="color:#e6db74">Access-Control-Allow-Origin</span> $http_origin <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">add_header</span> <span style="color:#e6db74">Vary</span> <span style="color:#e6db74">&#34;Origin&#34;</span> <span style="color:#e6db74">always</span>;
</span></span></code></pre></div><p>…and ensured no other <code>add_header</code> created duplicates. After that, <code>/sfu/get</code> succeeded and the WebSocket to <code>wss://rtc.example.com</code> immediately followed (I saw <strong>HTTP 101</strong> in the logs).</p>
<h3 id="step-3-confirm-livekit-side">Step 3: confirm LiveKit side</h3>
<p>Once the WS was up, LiveKit logs showed participants joining (not just <code>RoomService.CreateRoom</code>), and calls were established.</p>
<hr>
<h2 id="useful-verification-commands">Useful verification commands</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Synapse features (expect MSC4140 true)</span>
</span></span><span style="display:flex;"><span>curl -s https://matrix.example.com/_matrix/client/versions | jq <span style="color:#e6db74">&#39;.unstable_features.&#34;org.matrix.msc4140&#34;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Well-known with RTC focus</span>
</span></span><span style="display:flex;"><span>curl -s https://matrix.example.com/.well-known/matrix/client | jq
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># TURN creds (with your access token)</span>
</span></span><span style="display:flex;"><span>curl -s -H <span style="color:#e6db74">&#34;Authorization: Bearer </span>$TOKEN<span style="color:#e6db74">&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> https://matrix.example.com/_matrix/client/v3/voip/turnServer | jq
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># JWT health</span>
</span></span><span style="display:flex;"><span>curl -si -X POST https://rtc.example.com/sfu/get
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># LiveKit simple HTTP probe</span>
</span></span><span style="display:flex;"><span>curl -si https://rtc.example.com | head
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Nginx logs (look for 101 Switching Protocols when a call starts)</span>
</span></span><span style="display:flex;"><span>sudo tail -f /var/log/nginx/rtc.access.log | grep <span style="color:#e6db74">&#39; 101 &#39;</span>
</span></span></code></pre></div><hr>
<h2 id="common-pitfalls-i-hit-these-so-you-dont-have-to">Common pitfalls (I hit these so you dont have to)</h2>
<ul>
<li><strong>Host Nginx vs Docker Nginx:</strong> If you already run Nginx on the host, dont also bind 80/443/8448 in a Docker Nginx — youll get <code>bind() ... already in use</code> and restart loops. Use <strong>host</strong> Nginx to reverse proxy to containers.</li>
<li><strong>Nginx <code>http2</code> directive:</strong> Old Nginx may not support the <code>http2</code> directive on <code>listen</code>. Use <code>listen 443 ssl;</code> (and add <code>http2</code> if your version supports it).</li>
<li><strong>Certificate name mismatch:</strong> Make sure <code>rtc.example.com</code>s vhost uses a certificate <strong>for that exact hostname</strong> (initially I had the <code>matrix.*</code> cert on <code>rtc.*</code> and curl complained).</li>
<li><strong>Postgres collation:</strong> Either initialize the cluster with <code>C</code> or use <code>allow_unsafe_locale: true</code> in Synapse DB config to get running quickly.</li>
<li><strong>CORS duplication on <code>/sfu/get</code>:</strong> Only ONE <code>Access-Control-Allow-Origin</code> header. If the upstream adds it too, use <code>proxy_hide_header Access-Control-Allow-Origin;</code> on the Nginx location.</li>
<li><strong>Cloudflare:</strong> Use <strong>DNS-only</strong> for <code>rtc.*</code>. Proxies can interfere with WS and UDP paths.</li>
<li><strong>Firewall:</strong> Open the LiveKit UDP range and TURN ports on both v4 and v6.</li>
</ul>
<hr>
<h2 id="final-checklist-print-me">Final checklist (print me)</h2>
<ul>
<li><input disabled="" type="checkbox"> <code>https://matrix.example.com/.well-known/matrix/client</code> returns <strong>both</strong> <code>m.homeserver.base_url</code> and <code>org.matrix.msc4143.rtc_foci</code> pointing to <code>https://rtc.example.com</code>.</li>
<li><input disabled="" type="checkbox"> <code>/_matrix/client/versions</code> shows <code>&quot;org.matrix.msc4140&quot;: true</code>.</li>
<li><input disabled="" type="checkbox"> <code>/sfu/get</code> <strong>preflight</strong> returns <strong>one</strong> <code>Access-Control-Allow-Origin</code> and <strong>200/204</strong>.</li>
<li><input disabled="" type="checkbox"> Starting a call creates <strong>HTTP 101</strong> entries to <code>wss://rtc.example.com</code> in <code>rtc.access.log</code>.</li>
<li><input disabled="" type="checkbox"> LiveKit logs show <strong>participants joining</strong> (not just CreateRoom).</li>
<li><input disabled="" type="checkbox"> <code>/voip/turnServer</code> returns time-limited TURN credentials.</li>
<li><input disabled="" type="checkbox"> Cloudflare set to <strong>DNS-only</strong> for <code>rtc.*</code>. UFW allows 7881/tcp and your LiveKit UDP range.</li>
</ul>
<hr>
<h3 id="credits--tooling">Credits &amp; tooling</h3>
<ul>
<li>Matrix Synapse, coTURN, LiveKit, Element Call.</li>
<li><code>curl</code>, <code>jq</code>, <code>docker compose logs</code>, Nginx access logs. These are your best friends.</li>
<li>The debugging breakthrough was catching CORS errors in the browser console and looking for <strong>HTTP 101</strong> in Nginx logs.</li>
</ul>
<p>Happy calling! 🎉</p>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/coturn/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/coturn/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/coturn/"></head></html>

View file

@ -0,0 +1,17 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Debugging | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/debugging/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/debugging/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/debugging/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/debugging/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Debugging"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Debugging"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/>Tags</a></div><h1>Debugging
<a href=/tags/debugging/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><header class=entry-header><h2 class=entry-hint-parent>Stealth Trojan VPN Behind Cloudflare Guide</h2></header><div class=entry-content><p>Introduction So you want a VPN that doesnt scream “I am a VPN” to every censor and firewall out there?
Welcome to the world of Trojan over WebSocket + TLS behind Cloudflare.
This guide not only shows you how to set it up but also sprinkles in some debugging magic so you can figure out why things break (and they will break, trust me).
Well anonymise domains and secrets, so substitute with your own:
...</p></div><footer class=entry-footer><span title='2025-09-09 11:05:00 +0200 +0200'>September 9, 2025</span>&nbsp;·&nbsp;4 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Stealth Trojan VPN Behind Cloudflare Guide" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/></a></article><article class="post-entry tag-entry"><figure class=entry-cover><img loading=lazy src=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/images/matrix-cover.png alt="Matrix, Signal but distributed"></figure><header class=entry-header><h2 class=entry-hint-parent>Self-hosting Matrix + Element Call with LiveKit: from zero to working (and the [not so!!] fun debugging along the way)</h2></header><div class=entry-content><p>TL;DR: The call kept saying “waiting for media” because the browser never opened a WebSocket to LiveKit. The root cause was duplicate Access-Control-Allow-Origin headers on /sfu/get (CORS), which stopped the JWT response. Fixing CORS and ensuring the WS proxy worked (HTTP 101 in logs) solved it.
What Im building A Matrix homeserver (Synapse) at matrix.example.com (replace with your domain). TURN/STUN (coTURN) for NAT traversal. Element Call backed by LiveKit, fronted by rtc.example.com. Nginx (host) as the single reverse proxy for everything. Cloudflare DNS (with rtc.* set to DNS-only, no orange cloud). UFW firewall opened for Matrix federation, TURN, and LiveKit media ports. I used Docker for Synapse, PostgreSQL, LiveKit and the JWT helper. I used host Nginx (not Nginx in Docker) to avoid port binding conflicts on 80/443/8448.
...</p></div><footer class=entry-footer><span title='2025-08-26 00:00:00 +0000 UTC'>August 26, 2025</span>&nbsp;·&nbsp;9 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/matrix_setup/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Self-hosting Matrix + Element Call with LiveKit: from zero to working (and the [not so!!] fun debugging along the way)" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/matrix_setup/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/debugging/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/debugging/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/debugging/"></head></html>

View file

@ -0,0 +1,13 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Docker | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/docker/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/docker/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/docker/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/docker/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Docker"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Docker"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/>Tags</a></div><h1>Docker
<a href=/tags/docker/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><header class=entry-header><h2 class=entry-hint-parent>Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases</h2></header><div class=entry-content><p>How I containerized a Java Minecraft server with Geyser, hit a /var space wall, and locked the server to the latest stable release.</p></div><footer class=entry-footer><span title='2025-09-29 09:06:29 +0000 UTC'>September 29, 2025</span>&nbsp;·&nbsp;3 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/></a></article><article class="post-entry tag-entry"><figure class=entry-cover><img loading=lazy src=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/images/hedgedoc-cover.png alt="HedgeDoc collaborative editing"></figure><header class=entry-header><h2 class=entry-hint-parent>Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt</h2></header><div class=entry-content><p>HedgeDoc is an open-source collaborative Markdown editor. Think Google Docs for Markdown: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post well walk through setting up your own instance on a server, secured with HTTPS.
Prerequisites A Linux server with Docker and Docker Compose A domain name pointing to your server (e.g. notes.alipourimjourneys.ir) Nginx installed for reverse proxying Certbot for Lets Encrypt certificates 1. Create the project directory mkdir ~/hedgedoc && cd ~/hedgedoc 2. Create .env POSTGRES_PASSWORD=ChangeThisStrongPassword HD_DOMAIN=notes.alipourimjourneys.ir Generate a strong password with:
...</p></div><footer class=entry-footer><span title='2025-08-29 00:00:00 +0000 UTC'>August 29, 2025</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,390 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Docker on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/docker/</link><description>Recent content in Docker on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Mon, 29 Sep 2025 09:06:29 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/docker/index.xml" rel="self" type="application/rss+xml"/><item><title>Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/</link><pubDate>Mon, 29 Sep 2025 09:06:29 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/</guid><description>How I containerized a Java Minecraft server with Geyser, hit a /var space wall, and locked the server to the latest stable release.</description><content:encoded><![CDATA[<h2 id="why">Why</h2>
<p>Ive been running a Java Minecraft world (with Bedrock players via <strong>Geyser</strong>) in <code>tmux</code>. I moved it to Docker for easier updates, backups, and restarts. Along the way I hit:</p>
<ul>
<li><code>failed to register layer: ... no space left on device</code></li>
<li>Client saying: <strong>“Outdated client, please use 1.21.9 Pre-Release 2”</strong></li>
<li>Wanting config in a neat <code>.env</code> and <strong>no whitelist</strong></li>
</ul>
<p>Heres exactly what I did.</p>
<hr>
<h2 id="folder-layout">Folder layout</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>~/minecraft/
</span></span><span style="display:flex;"><span>├─ docker-compose.yml
</span></span><span style="display:flex;"><span>├─ .env
</span></span><span style="display:flex;"><span>└─ plugins/
</span></span></code></pre></div><hr>
<h2 id="env-secrets--knobs"><code>.env</code> (secrets &amp; knobs)</h2>
<p>Use the <strong>latest stable</strong> (not snapshots/pre-releases) by setting <code>VERSION=LATEST</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-env" data-lang="env"><span style="display:flex;"><span><span style="color:#75715e"># Minecraft server configuration</span>
</span></span><span style="display:flex;"><span>EULA<span style="color:#f92672">=</span>TRUE
</span></span><span style="display:flex;"><span>TYPE<span style="color:#f92672">=</span>PAPER
</span></span><span style="display:flex;"><span>VERSION<span style="color:#f92672">=</span>LATEST
</span></span><span style="display:flex;"><span>MEMORY<span style="color:#f92672">=</span>4G
</span></span><span style="display:flex;"><span>USE_AIKAR_FLAGS<span style="color:#f92672">=</span>true
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># RCON (remote console)</span>
</span></span><span style="display:flex;"><span>ENABLE_RCON<span style="color:#f92672">=</span>true
</span></span><span style="display:flex;"><span>RCON_PASSWORD<span style="color:#f92672">=</span>superSecretPassword123
</span></span></code></pre></div><blockquote>
<p>I dont use a whitelist, so no <code>WHITELIST</code>/<code>ENFORCE_WHITELIST</code> variables.</p></blockquote>
<hr>
<h2 id="docker-composeyml"><code>docker-compose.yml</code></h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">mc</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">itzg/minecraft-server:latest</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">mc</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;25565:25565&#34;</span> <span style="color:#75715e"># Java</span>
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;19132:19132/udp&#34;</span> <span style="color:#75715e"># Bedrock via Geyser plugin</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">env_file</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">.env</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">mc-data:/data</span>
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">./plugins:/plugins:ro</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">mc-data</span>: {}
</span></span></code></pre></div><blockquote>
<p>The <code>version:</code> key in Compose is obsolete now, so I dropped it.</p></blockquote>
<hr>
<h2 id="add-geyser-and-optional-floodgate-as-plugins">Add Geyser (and optional Floodgate) as plugins</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir -p ~/minecraft/plugins
</span></span><span style="display:flex;"><span>cd ~/minecraft/plugins
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Geyser (Spigot/Paper)</span>
</span></span><span style="display:flex;"><span>wget https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot -O Geyser-Spigot.jar
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Floodgate (optional: Bedrock accounts without Java linking)</span>
</span></span><span style="display:flex;"><span>wget https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot -O Floodgate-Spigot.jar
</span></span></code></pre></div><p>Start it up:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose up -d
</span></span></code></pre></div><p>On first run, Geyser writes its config to <code>/data/plugins/Geyser-Spigot/</code>. Open UDP <strong>19132</strong> on your firewall.</p>
<hr>
<h2 id="hit-a-wall-var-ran-out-of-space">Hit a wall: <code>/var</code> ran out of space</h2>
<p>Docker stores layers at <code>/var/lib/docker</code> by default. My <code>/var</code> LV was tiny:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 448 25"
>
<g transform='translate(8,16)'>
<path d='M 404,8 L 404,24' fill='none' stroke='currentColor'></path>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>%</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>r</text>
</g>
</svg>
</div>
<h3 id="quick-cleanup">Quick cleanup</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker system df
</span></span><span style="display:flex;"><span>docker system prune -f
</span></span><span style="display:flex;"><span>docker builder prune -af
</span></span><span style="display:flex;"><span>sudo apt-get clean
</span></span><span style="display:flex;"><span>sudo journalctl --vacuum-size<span style="color:#f92672">=</span>100M
</span></span></code></pre></div><p>If thats not enough, you have two good options:</p>
<h3 id="option-a--move-dockers-data-off-var">Option A — Move Dockers data off <code>/var</code></h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo systemctl stop docker
</span></span><span style="display:flex;"><span>sudo mkdir -p /home/docker
</span></span><span style="display:flex;"><span>sudo rsync -aHAX --info<span style="color:#f92672">=</span>progress2 /var/lib/docker/ /home/docker/
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#39;{ &#34;data-root&#34;: &#34;/home/docker&#34; }&#39;</span> | sudo tee /etc/docker/daemon.json
</span></span><span style="display:flex;"><span>sudo systemctl start docker
</span></span><span style="display:flex;"><span>docker info | grep <span style="color:#e6db74">&#34;Docker Root Dir&#34;</span>
</span></span></code></pre></div><p>If all looks good, free the old space:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo rm -rf /var/lib/docker/*
</span></span></code></pre></div><h3 id="option-b--grow-var-lvm">Option B — Grow <code>/var</code> (LVM)</h3>
<p>Check free space in the VG:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo vgdisplay <span style="color:#75715e"># look for &#34;Free PE / Size&#34;</span>
</span></span></code></pre></div><p>If available, extend <code>/var</code> by +5G:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo lvextend -L +5G /dev/mapper/ubuntu--vg-var
</span></span><span style="display:flex;"><span>sudo resize2fs /dev/mapper/ubuntu--vg-var
</span></span></code></pre></div><hr>
<h2 id="the-version-mismatch-pre-release-vs-stable">The version mismatch: pre-release vs stable</h2>
<p>My logs showed:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 440 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>2</text>
</g>
</svg>
</div>
<p>That happens if the server pulls a pre-release build (or if <code>VERSION=LATEST</code>). Fix:</p>
<ol>
<li>Ensure <code>.env</code> has:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-env" data-lang="env"><span style="display:flex;"><span>VERSION<span style="color:#f92672">=</span>LATEST_RELEASE
</span></span></code></pre></div></li>
<li>Recreate:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose down
</span></span><span style="display:flex;"><span>docker compose pull
</span></span><span style="display:flex;"><span>docker compose up -d
</span></span></code></pre></div></li>
<li>Verify:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker logs mc | grep <span style="color:#e6db74">&#34;Starting minecraft server version&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># -&gt; Starting minecraft server version 1.21.1 (example)</span>
</span></span></code></pre></div></li>
</ol>
<blockquote>
<p>Tip: If you <strong>want to pin</strong> and avoid auto-updates entirely, set <code>VERSION=1.21.1</code> (or whatever stable youve validated).</p></blockquote>
<hr>
<h2 id="no-whitelist">No whitelist</h2>
<p>Because I dont set <code>WHITELIST</code>/<code>ENFORCE_WHITELIST</code>, anyone can join (subject to online-mode, bans, and Geyser/Floodgate auth settings). Manage ops/permissions via:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker exec -it mc rcon-cli <span style="color:#e6db74">&#34;op YourJavaIGN&#34;</span>
</span></span></code></pre></div><p>(or edit <code>/data/ops.json</code>).</p>
<hr>
<h2 id="backup--updates">Backup &amp; updates</h2>
<ul>
<li>World/data live under the <code>mc-data</code> volume → back up <code>/data</code> regularly.</li>
<li>Update cleanly:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose pull <span style="color:#f92672">&amp;&amp;</span> docker compose up -d
</span></span></code></pre></div></li>
<li>Watch space:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>watch -n5 <span style="color:#e6db74">&#39;df -h /var; docker system df&#39;</span>
</span></span></code></pre></div></li>
</ul>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Put <strong>Geyser/Floodgate</strong> jars in <code>./plugins</code> and expose <strong>19132/udp</strong>.</li>
<li>Keep configs in <code>.env</code>.</li>
<li>Fix <code>/var</code> space by pruning, <strong>moving Dockers data-root</strong>, or <strong>extending <code>/var</code></strong> via LVM.</li>
<li>Verify version in logs after each change.</li>
</ul>
<p>Happy block-breaking! 🧱🚀</p>
]]></content:encoded></item><item><title>Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/</link><pubDate>Fri, 29 Aug 2025 00:00:00 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/</guid><description>&lt;p>HedgeDoc is an open-source collaborative Markdown editor. Think &lt;em>Google Docs for Markdown&lt;/em>: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post well walk through setting up your own instance on a server, secured with HTTPS.&lt;/p>
&lt;hr>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;ul>
&lt;li>A Linux server with &lt;strong>Docker&lt;/strong> and &lt;strong>Docker Compose&lt;/strong>&lt;/li>
&lt;li>A domain name pointing to your server (e.g. &lt;code>notes.alipourimjourneys.ir&lt;/code>)&lt;/li>
&lt;li>&lt;strong>Nginx&lt;/strong> installed for reverse proxying&lt;/li>
&lt;li>&lt;strong>Certbot&lt;/strong> for Lets Encrypt certificates&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="1-create-the-project-directory">1. Create the project directory&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>mkdir ~/hedgedoc &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> cd ~/hedgedoc&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;hr>
&lt;h2 id="2-create-env">2. Create &lt;code>.env&lt;/code>&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-env" data-lang="env">&lt;span style="display:flex;">&lt;span>POSTGRES_PASSWORD&lt;span style="color:#f92672">=&lt;/span>ChangeThisStrongPassword
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>HD_DOMAIN&lt;span style="color:#f92672">=&lt;/span>notes.alipourimjourneys.ir&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Generate a strong password with:&lt;/p></description><content:encoded><![CDATA[<p>HedgeDoc is an open-source collaborative Markdown editor. Think <em>Google Docs for Markdown</em>: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post well walk through setting up your own instance on a server, secured with HTTPS.</p>
<hr>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>A Linux server with <strong>Docker</strong> and <strong>Docker Compose</strong></li>
<li>A domain name pointing to your server (e.g. <code>notes.alipourimjourneys.ir</code>)</li>
<li><strong>Nginx</strong> installed for reverse proxying</li>
<li><strong>Certbot</strong> for Lets Encrypt certificates</li>
</ul>
<hr>
<h2 id="1-create-the-project-directory">1. Create the project directory</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir ~/hedgedoc <span style="color:#f92672">&amp;&amp;</span> cd ~/hedgedoc</span></span></code></pre></div>
<hr>
<h2 id="2-create-env">2. Create <code>.env</code></h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-env" data-lang="env"><span style="display:flex;"><span>POSTGRES_PASSWORD<span style="color:#f92672">=</span>ChangeThisStrongPassword
</span></span><span style="display:flex;"><span>HD_DOMAIN<span style="color:#f92672">=</span>notes.alipourimjourneys.ir</span></span></code></pre></div>
<p>Generate a strong password with:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>openssl rand -base64 <span style="color:#ae81ff">32</span></span></span></code></pre></div>
<hr>
<h2 id="3-create-docker-composeyml">3. Create <code>docker-compose.yml</code></h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">version</span>: <span style="color:#e6db74">&#34;3.9&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">db</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">postgres:16</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_USER</span>: <span style="color:#ae81ff">hedgedoc</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_PASSWORD</span>: <span style="color:#ae81ff">${POSTGRES_PASSWORD}</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_DB</span>: <span style="color:#ae81ff">hedgedoc</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">db:/var/lib/postgresql/data</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">hedgedoc</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">quay.io/hedgedoc/hedgedoc:1.10.2</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">depends_on</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">db</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_DB_URL</span>: <span style="color:#ae81ff">postgres://hedgedoc:${POSTGRES_PASSWORD}@db:5432/hedgedoc</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_DOMAIN</span>: <span style="color:#ae81ff">${HD_DOMAIN}</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_PROTOCOL_USESSL</span>: <span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_URL_ADDPORT</span>: <span style="color:#e6db74">&#34;false&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_PORT</span>: <span style="color:#e6db74">&#34;3000&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_EMAIL</span>: <span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_ALLOW_EMAIL_REGISTER</span>: <span style="color:#e6db74">&#34;false&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">uploads:/hedgedoc/public/uploads</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;127.0.0.1:3000:3000&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">db</span>:
</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">uploads:</span></span></span></code></pre></div>
<p>Bring it up:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose up -d</span></span></code></pre></div>
<hr>
<h2 id="4-get-a-lets-encrypt-certificate">4. Get a Lets Encrypt certificate</h2>
<p>Request a cert with a <strong>DNS challenge</strong>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo certbot certonly --manual --preferred-challenges dns -d notes.alipourimjourneys.ir -m you@example.com --agree-tos --no-eff-email</span></span></code></pre></div>
<p>Add the TXT record certbot asks for, wait for DNS to propagate, then continue.<br>
Certificates will be in:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>/etc/letsencrypt/live/notes.alipourimjourneys.ir/</span></span></code></pre></div>
<hr>
<h2 id="5-configure-nginx">5. Configure Nginx</h2>
<p>Create <code>/etc/nginx/sites-available/notes.alipourimjourneys.ir</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">notes.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">80</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:80</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">return</span> <span style="color:#ae81ff">301</span> <span style="color:#e6db74">https://</span>$host$request_uri;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">notes.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/notes.alipourimjourneys.ir/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/notes.alipourimjourneys.ir/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:3000</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Real-IP</span> $remote_addr;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $proxy_add_x_forwarded_for;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> <span style="color:#e6db74">https</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_http_version</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">.1</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">&#34;upgrade&#34;</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p>Enable the config and reload Nginx:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo ln -s /etc/nginx/sites-available/notes.alipourimjourneys.ir /etc/nginx/sites-enabled/
</span></span><span style="display:flex;"><span>sudo nginx -t <span style="color:#f92672">&amp;&amp;</span> sudo systemctl reload nginx</span></span></code></pre></div>
<hr>
<h2 id="6-create-users">6. Create users</h2>
<p>Because we disabled self-registration, create accounts manually:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose exec hedgedoc ./bin/manage_users --add alice@example.com</span></span></code></pre></div>
<p>Youll be prompted for a password.<br>
To reset a password later:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose exec hedgedoc ./bin/manage_users --reset alice@example.com</span></span></code></pre></div>
<hr>
<h2 id="7-done">7. Done!</h2>
<p>Visit <a href="https://notes.alipourimjourneys.ir">https://notes.alipourimjourneys.ir</a> and log in with the user you created. You now have your own collaborative Markdown editor 🎉</p>
<hr>
<p><strong>Extras:</strong></p>
<ul>
<li>Backups: dump the PostgreSQL database and save the <code>uploads</code> volume.</li>
<li>Upgrades: <code>docker pull quay.io/hedgedoc/hedgedoc:latest &amp;&amp; docker compose up -d</code>.</li>
<li>Integrations: HedgeDoc supports S3/MinIO image storage, GitHub/GitLab/Google login, and more.</li>
</ul>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/docker/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/docker/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/docker/"></head></html>

View file

@ -0,0 +1,12 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Element-Call | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/element-call/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/element-call/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/element-call/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/element-call/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Element-Call"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Element-Call"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/>Tags</a></div><h1>Element-Call
<a href=/tags/element-call/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><figure class=entry-cover><img loading=lazy src=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/images/matrix-cover.png alt="Matrix, Signal but distributed"></figure><header class=entry-header><h2 class=entry-hint-parent>Self-hosting Matrix + Element Call with LiveKit: from zero to working (and the [not so!!] fun debugging along the way)</h2></header><div class=entry-content><p>TL;DR: The call kept saying “waiting for media” because the browser never opened a WebSocket to LiveKit. The root cause was duplicate Access-Control-Allow-Origin headers on /sfu/get (CORS), which stopped the JWT response. Fixing CORS and ensuring the WS proxy worked (HTTP 101 in logs) solved it.
What Im building A Matrix homeserver (Synapse) at matrix.example.com (replace with your domain). TURN/STUN (coTURN) for NAT traversal. Element Call backed by LiveKit, fronted by rtc.example.com. Nginx (host) as the single reverse proxy for everything. Cloudflare DNS (with rtc.* set to DNS-only, no orange cloud). UFW firewall opened for Matrix federation, TURN, and LiveKit media ports. I used Docker for Synapse, PostgreSQL, LiveKit and the JWT helper. I used host Nginx (not Nginx in Docker) to avoid port binding conflicts on 80/443/8448.
...</p></div><footer class=entry-footer><span title='2025-08-26 00:00:00 +0000 UTC'>August 26, 2025</span>&nbsp;·&nbsp;9 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/matrix_setup/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Self-hosting Matrix + Element Call with LiveKit: from zero to working (and the [not so!!] fun debugging along the way)" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/matrix_setup/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,639 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Element-Call on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/element-call/</link><description>Recent content in Element-Call on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Tue, 26 Aug 2025 00:00:00 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/element-call/index.xml" rel="self" type="application/rss+xml"/><item><title>Self-hosting Matrix + Element Call with LiveKit: from zero to working (and the [not so!!] fun debugging along the way)</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/matrix_setup/</link><pubDate>Tue, 26 Aug 2025 00:00:00 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/matrix_setup/</guid><description>How I set up a Matrix homeserver (Synapse) with TURN and Element Call using LiveKit, plus the exact debugging steps that took it from &amp;#39;waiting for media&amp;#39; to solid calls.</description><content:encoded><![CDATA[<blockquote>
<p>TL;DR: The call kept saying <strong>“waiting for media”</strong> because the browser never opened a WebSocket to LiveKit. The root cause was <strong>duplicate <code>Access-Control-Allow-Origin</code> headers</strong> on <code>/sfu/get</code> (CORS), which stopped the JWT response. Fixing CORS and ensuring the WS proxy worked (HTTP <strong>101</strong> in logs) solved it.</p></blockquote>
<h2 id="what-im-building">What I&rsquo;m building</h2>
<ul>
<li>A <strong>Matrix homeserver</strong> (Synapse) at <code>matrix.example.com</code> (replace with your domain).</li>
<li><strong>TURN/STUN</strong> (coTURN) for NAT traversal.</li>
<li><strong>Element Call</strong> backed by <strong>LiveKit</strong>, fronted by <code>rtc.example.com</code>.</li>
<li><strong>Nginx (host)</strong> as the single reverse proxy for everything.</li>
<li><strong>Cloudflare</strong> DNS (with <code>rtc.*</code> set to <strong>DNS-only</strong>, no orange cloud).</li>
<li><strong>UFW</strong> firewall opened for Matrix federation, TURN, and LiveKit media ports.</li>
</ul>
<blockquote>
<p>I used Docker for Synapse, PostgreSQL, LiveKit and the JWT helper. I used <strong>host</strong> Nginx (not Nginx in Docker) to avoid port binding conflicts on 80/443/8448.</p></blockquote>
<hr>
<h2 id="prereqs">Prereqs</h2>
<ul>
<li>DNS A/AAAA:
<ul>
<li><code>matrix.example.com</code> → your server (v4/v6)</li>
<li><code>rtc.example.com</code> → your server (v4/v6)</li>
</ul>
</li>
<li>Certificates:
<ul>
<li><code>matrix.example.com</code> and <code>rtc.example.com</code> via Lets Encrypt on the host</li>
</ul>
</li>
<li>Cloudflare: <strong>DNS-only (grey cloud)</strong> for <code>rtc.example.com</code> so WebSockets &amp; UDP work without interference.</li>
<li>UFW / firewall open:
<ul>
<li>80/tcp, 443/tcp</li>
<li>8448/tcp (Matrix federation)</li>
<li>3478/tcp, 3478/udp and <strong>5349/tcp</strong> (TURN/TLS)</li>
<li><strong>LiveKit</strong>: 7881/tcp and <strong>5010050200/udp</strong> (or your chosen range)</li>
</ul>
</li>
<li>Docker + docker compose installed.</li>
</ul>
<hr>
<h2 id="synapse--postgresql">Synapse + PostgreSQL</h2>
<h3 id="1-the-postgresql-collation-gotcha">1) The PostgreSQL collation gotcha</h3>
<p>Synapse prefers the database collation <strong><code>C</code></strong>. If your Postgres cluster was initialized with <code>en_US.utf8</code>, Synapse will error like:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 512 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>'</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>U</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>8</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>'</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>'</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>'</text>
</g>
</svg>
</div>
<p><strong>Two ways to resolve:</strong></p>
<ul>
<li><strong>Preferred (clean)</strong>: Re-initialize the Postgres <strong>cluster</strong> with <code>C</code> and <code>UTF-8</code>:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># docker-compose.yml (excerpt for Postgres)</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">db</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">postgres:16</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_DB</span>: <span style="color:#ae81ff">synapse</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_USER</span>: <span style="color:#ae81ff">synapse</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_PASSWORD</span>: <span style="color:#ae81ff">&lt;strong-password&gt;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_INITDB_ARGS</span>: <span style="color:#e6db74">&#34;--locale=C --encoding=UTF8 --lc-collate=C --lc-ctype=C&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">./pgdata:/var/lib/postgresql/data</span>
</span></span></code></pre></div><blockquote>
<p>Requires wiping the volume and recreating the DB.</p></blockquote>
<ul>
<li><strong>Pragmatic (works quickly)</strong>: In Synapses DB config, set <code>allow_unsafe_locale: true</code>. This bypasses the check. Its fine for hobby use; for production, prefer the clean <code>C</code> cluster.</li>
</ul>
<h3 id="2-start-synapse-and-generate-config">2) Start Synapse and generate config</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose up -d db synapse
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Logs</span>
</span></span><span style="display:flex;"><span>docker compose logs --tail<span style="color:#f92672">=</span><span style="color:#ae81ff">200</span> synapse
</span></span></code></pre></div><p>Ensure Synapse prints your <strong>server_name</strong> and <strong>public base URL</strong> and stays up.</p>
<h3 id="3-create-an-admin-user">3) Create an admin user</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Exec into the running Synapse container:</span>
</span></span><span style="display:flex;"><span>docker compose exec synapse register_new_matrix_user <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -c /data/homeserver.yaml -u &lt;username&gt; -p &lt;password&gt; <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -a -k
</span></span></code></pre></div><blockquote>
<p>If you see “Unknown execution mode”, you probably ran the binary with the wrong entrypoint. Use <code>docker compose exec synapse …</code> against the running container.</p></blockquote>
<hr>
<h2 id="turn-coturn">TURN (coTURN)</h2>
<h3 id="1-avoid-bad-inline-comments">1) Avoid bad inline comments</h3>
<p>If you see errors like:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 808 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>U</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>Y</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='512' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='520' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='528' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='544' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='552' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='560' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='568' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='576' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='584' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='592' y='4' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='608' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='616' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='624' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='632' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='640' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='648' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='656' y='4' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='672' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='680' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='688' y='4' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='696' y='4' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='712' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='720' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='728' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='736' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='744' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='752' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='760' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='768' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='776' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='784' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='792' y='4' fill='currentColor' style='font-size:1em'>.</text>
</g>
</svg>
</div>
<p>…it means a <code>#</code> comment is on the <strong>same line</strong> as a boolean directive. Move comments to their own lines.</p>
<h3 id="2-minimal-turnserverconf">2) Minimal <code>turnserver.conf</code></h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#a6e22e">listening-port</span><span style="color:#f92672">=</span><span style="color:#e6db74">3478</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">tls-listening-port</span><span style="color:#f92672">=</span><span style="color:#e6db74">5349</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">fingerprint</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">use-auth-secret</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">static-auth-secret</span><span style="color:#f92672">=</span><span style="color:#e6db74">&lt;shared-secret&gt; # also set in Synapse</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">realm</span><span style="color:#f92672">=</span><span style="color:#e6db74">example.com # used in creds generation</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">total-quota</span><span style="color:#f92672">=</span><span style="color:#e6db74">0</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">bps-capacity</span><span style="color:#f92672">=</span><span style="color:#e6db74">0</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">cli-password</span><span style="color:#f92672">=</span><span style="color:#e6db74">&lt;admin-pass&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">no-cli</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">cert</span><span style="color:#f92672">=</span><span style="color:#e6db74">/etc/letsencrypt/live/turn.example.com/fullchain.pem</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">pkey</span><span style="color:#f92672">=</span><span style="color:#e6db74">/etc/letsencrypt/live/turn.example.com/privkey.pem</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># If behind NAT:</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># external-ip=&lt;public-ip&gt;/&lt;internal-ip&gt;</span>
</span></span></code></pre></div><h3 id="3-wire-turn-into-synapse">3) Wire TURN into Synapse</h3>
<p>In <code>homeserver.yaml</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">turn_uris</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;turn:turn.example.com?transport=udp&#34;</span>
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;turn:turn.example.com?transport=tcp&#34;</span>
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;turns:turn.example.com:5349?transport=tcp&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">turn_shared_secret</span>: <span style="color:#e6db74">&#34;&lt;shared-secret&gt;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">turn_user_lifetime</span>: <span style="color:#e6db74">&#34;1d&#34;</span>
</span></span></code></pre></div><p>Verify the homeserver issues TURN creds:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>TOKEN<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;&lt;your matrix access token&gt;&#39;</span>
</span></span><span style="display:flex;"><span>curl -s -H <span style="color:#e6db74">&#34;Authorization: Bearer </span>$TOKEN<span style="color:#e6db74">&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> https://matrix.example.com/_matrix/client/v3/voip/turnServer | jq
</span></span></code></pre></div><p>You should see <code>uris</code>, a time-limited <code>username</code> and <code>password</code>.</p>
<hr>
<h2 id="nginx-host-for-synapse-and-federation">Nginx (host) for Synapse and federation</h2>
<p>Create <code>/etc/nginx/conf.d/matrix.conf</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#75715e"># Client traffic on 443
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:443</span> <span style="color:#e6db74">ssl</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">matrix.example.com</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/matrix.example.com/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/matrix.example.com/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Proxy client &amp; admin APIs to Synapse (container) on 8008
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> ~ <span style="color:#e6db74">^(/_matrix|/_synapse/client)</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:8008</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $remote_addr;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">client_max_body_size</span> <span style="color:#e6db74">50M</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Advertise homeserver base + RTC focus (MSC4143) via .well-known
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> = <span style="color:#e6db74">/.well-known/matrix/client</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">default_type</span> <span style="color:#e6db74">application/json</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Access-Control-Allow-Origin</span> <span style="color:#e6db74">&#34;*&#34;</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">return</span> <span style="color:#ae81ff">200</span> <span style="color:#e6db74">&#39;</span>{<span style="color:#f92672">&#34;m.homeserver&#34;:{&#34;base_url&#34;:&#34;https://matrix.example.com&#34;},&#34;org.matrix.msc4143.rtc_foci&#34;:[{&#34;type&#34;:&#34;livekit&#34;,&#34;livekit_service_url&#34;:&#34;https://rtc.example.com&#34;}]}&#39;</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Federation on 8448
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#f92672">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">8448</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:8448</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">matrix.example.com</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/matrix.example.com/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/matrix.example.com/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:8008</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $remote_addr;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">client_max_body_size</span> <span style="color:#e6db74">50M</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Reload and sanity check:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo nginx -t <span style="color:#f92672">&amp;&amp;</span> sudo systemctl reload nginx
</span></span><span style="display:flex;"><span>curl -s https://matrix.example.com/.well-known/matrix/client | jq
</span></span></code></pre></div><p>Also check Synapse supports RTC signaling (MSC4140) so clients actually use it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -s https://matrix.example.com/_matrix/client/versions | jq <span style="color:#e6db74">&#39;.unstable_features.&#34;org.matrix.msc4140&#34;&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># expect: true</span>
</span></span></code></pre></div><hr>
<h2 id="element-call--livekit">Element Call + LiveKit</h2>
<p>Ill run <strong>LiveKit</strong> and the small <strong>JWT helper</strong> (Elements <code>elementcall_jwt</code>) in Docker. LiveKit handles media; the JWT helper mints access tokens for WebSocket connects.</p>
<h3 id="1-livekit-config-etclivekityaml-inside-container">1) LiveKit config (<code>/etc/livekit.yaml</code> inside container)</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">port</span>: <span style="color:#ae81ff">7880</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">bind_addresses</span>: [<span style="color:#e6db74">&#34;0.0.0.0&#34;</span>]
</span></span><span style="display:flex;"><span><span style="color:#f92672">rtc</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">tcp_port</span>: <span style="color:#ae81ff">7881</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">port_range_start</span>: <span style="color:#ae81ff">50100</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">port_range_end</span>: <span style="color:#ae81ff">50200</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">use_external_ip</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">logging</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">level</span>: <span style="color:#ae81ff">info</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">turn</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">enabled</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">keys</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">lk_prod_1</span>: <span style="color:#e6db74">&#34;REPLACE_WITH_A_64_CHAR_RANDOM_SECRET___________________________________&#34;</span>
</span></span></code></pre></div><blockquote>
<p><strong>Important:</strong> the secret must be <strong>&gt;= 32 chars</strong>. The default <code>devkey</code> will trigger <code>secret is too short</code> warnings and wont work with the JWT helper.</p></blockquote>
<h3 id="2-elementcall_jwt-env">2) elementcall_jwt env</h3>
<p>Run it with:</p>
<ul>
<li><code>LIVEKIT_URL=wss://rtc.example.com</code> <em>(root WS URL, <strong>no</strong> <code>/livekit/sfu</code> path)</em></li>
<li><code>LIVEKIT_KEY=lk_prod_1</code></li>
<li><code>LIVEKIT_SECRET=&lt;the long secret above&gt;</code></li>
<li><code>LIVEKIT_JWT_PORT=8080</code> (internal HTTP port the proxy will hit)</li>
<li>Optionally: <code>LIVEKIT_FULL_ACCESS_HOMESERVERS=*</code> during setup</li>
</ul>
<p>Check logs on start; it prints the LIVEKIT_URL it will advertise.</p>
<h3 id="3-nginx-host-for-rtcexamplecom">3) Nginx (host) for <code>rtc.example.com</code></h3>
<p>Create <code>/etc/nginx/conf.d/rtc.conf</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:443</span> <span style="color:#e6db74">ssl</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">rtc.example.com</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/rtc.example.com/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/rtc.example.com/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">access_log</span> <span style="color:#e6db74">/var/log/nginx/rtc.access.log</span> <span style="color:#e6db74">combined</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># 3a) JWT endpoint with clean CORS (avoid duplicate ACAO)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> = <span style="color:#e6db74">/sfu/get</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_hide_header</span> <span style="color:#e6db74">Access-Control-Allow-Origin</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Access-Control-Allow-Origin</span> $http_origin <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Vary</span> <span style="color:#e6db74">&#34;Origin&#34;</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Access-Control-Allow-Methods</span> <span style="color:#e6db74">&#34;POST,</span> <span style="color:#e6db74">OPTIONS&#34;</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Access-Control-Allow-Headers</span> <span style="color:#e6db74">&#34;Accept,</span> <span style="color:#e6db74">Content-Type,</span> <span style="color:#e6db74">Content-Length,</span> <span style="color:#e6db74">Accept-Encoding,</span> <span style="color:#e6db74">X-CSRF-Token,</span> <span style="color:#e6db74">Authorization&#34;</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">if</span> <span style="color:#e6db74">(</span>$request_method = <span style="color:#e6db74">OPTIONS)</span> { <span style="color:#f92672">return</span> <span style="color:#ae81ff">204</span>; }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:8070/sfu/get</span>; <span style="color:#75715e"># elementcall_jwt
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># 3b) LiveKit WS &amp; HTTP (catch-all)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_http_version</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">.1</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">&#34;upgrade&#34;</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Sec-WebSocket-Protocol</span> $http_sec_websocket_protocol; <span style="color:#75715e"># &#34;livekit&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Origin</span> $http_origin;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_buffering</span> <span style="color:#66d9ef">off</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_read_timeout</span> <span style="color:#e6db74">3600s</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:7880</span>; <span style="color:#75715e"># livekit
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Reload and basic checks:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo nginx -t <span style="color:#f92672">&amp;&amp;</span> sudo systemctl reload nginx
</span></span><span style="display:flex;"><span><span style="color:#75715e"># JWT preflight (should return a single ACAO header)</span>
</span></span><span style="display:flex;"><span>curl -si -X OPTIONS https://rtc.example.com/sfu/get <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Origin: https://app.element.io&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Access-Control-Request-Method: POST&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Access-Control-Request-Headers: authorization, content-type&#39;</span> | sed -n <span style="color:#e6db74">&#39;1,30p&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Expected: one Access-Control-Allow-Origin and 200/204</span>
</span></span></code></pre></div><blockquote>
<p><strong>Why I did this:</strong> I initially had <strong>two</strong> <code>Access-Control-Allow-Origin</code> headers (one added by the upstream, one by Nginx). Browsers reject that with “Access-Control-Allow-Origin cannot contain more than one origin”, so the JWT response never reached the client. Fixing CORS fixed everything.</p></blockquote>
<hr>
<h2 id="the-waiting-for-media-debugging-story-how-i-found-it">The “waiting for media” debugging story (how I found it)</h2>
<p>Symptom: Element Call created rooms, but calls stayed on <strong>“waiting for media.”</strong></p>
<p>What worked:</p>
<ul>
<li><code>elementcall_jwt</code> could <strong>CreateRoom</strong> in LiveKit (seen in logs).</li>
<li>TURN creds endpoint returned time-limited credentials.</li>
</ul>
<p>What didnt appear:</p>
<ul>
<li>No <strong>HTTP 101</strong> lines in <code>rtc.access.log</code> → the <strong>browser never established a WebSocket</strong> to LiveKit.</li>
</ul>
<h3 id="step-1-prove-the-ws-vhost-works-even-without-auth">Step 1: prove the WS vhost works (even without auth)</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -v --http1.1 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Connection: Upgrade&#39;</span> -H <span style="color:#e6db74">&#39;Upgrade: websocket&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Sec-WebSocket-Version: 13&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> https://rtc.example.com/rtc -o /dev/null
</span></span></code></pre></div><p>Result: I got a <code>401</code> (expected), but importantly I saw a log line in <code>rtc.access.log</code>. So <strong>Nginx WS proxying was fine</strong>.</p>
<h3 id="step-2-check-the-browser-console">Step 2: check the browser console</h3>
<p>The smoking gun in DevTools:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 672 41"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>F</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>I</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='160' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='280' y='20' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='288' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='296' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='304' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='312' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='320' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='328' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='336' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='344' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='352' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='360' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='368' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='376' y='20' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='384' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='392' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='400' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='408' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='416' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='432' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='440' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='448' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='464' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='472' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='488' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='496' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='504' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='504' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='512' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='520' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='528' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='544' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='552' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='560' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='568' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='576' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='584' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='592' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='608' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='616' y='20' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='624' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='632' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='640' y='20' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='648' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='656' y='20' fill='currentColor' style='font-size:1em'>.</text>
</g>
</svg>
</div>
<p>The browser refused the JWT call due to duplicated <strong>ACAO</strong> headers, so no token → no WebSocket connect.</p>
<p><strong>Fix:</strong> in Nginx I added:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">proxy_hide_header</span> <span style="color:#e6db74">Access-Control-Allow-Origin</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">add_header</span> <span style="color:#e6db74">Access-Control-Allow-Origin</span> $http_origin <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">add_header</span> <span style="color:#e6db74">Vary</span> <span style="color:#e6db74">&#34;Origin&#34;</span> <span style="color:#e6db74">always</span>;
</span></span></code></pre></div><p>…and ensured no other <code>add_header</code> created duplicates. After that, <code>/sfu/get</code> succeeded and the WebSocket to <code>wss://rtc.example.com</code> immediately followed (I saw <strong>HTTP 101</strong> in the logs).</p>
<h3 id="step-3-confirm-livekit-side">Step 3: confirm LiveKit side</h3>
<p>Once the WS was up, LiveKit logs showed participants joining (not just <code>RoomService.CreateRoom</code>), and calls were established.</p>
<hr>
<h2 id="useful-verification-commands">Useful verification commands</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Synapse features (expect MSC4140 true)</span>
</span></span><span style="display:flex;"><span>curl -s https://matrix.example.com/_matrix/client/versions | jq <span style="color:#e6db74">&#39;.unstable_features.&#34;org.matrix.msc4140&#34;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Well-known with RTC focus</span>
</span></span><span style="display:flex;"><span>curl -s https://matrix.example.com/.well-known/matrix/client | jq
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># TURN creds (with your access token)</span>
</span></span><span style="display:flex;"><span>curl -s -H <span style="color:#e6db74">&#34;Authorization: Bearer </span>$TOKEN<span style="color:#e6db74">&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> https://matrix.example.com/_matrix/client/v3/voip/turnServer | jq
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># JWT health</span>
</span></span><span style="display:flex;"><span>curl -si -X POST https://rtc.example.com/sfu/get
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># LiveKit simple HTTP probe</span>
</span></span><span style="display:flex;"><span>curl -si https://rtc.example.com | head
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Nginx logs (look for 101 Switching Protocols when a call starts)</span>
</span></span><span style="display:flex;"><span>sudo tail -f /var/log/nginx/rtc.access.log | grep <span style="color:#e6db74">&#39; 101 &#39;</span>
</span></span></code></pre></div><hr>
<h2 id="common-pitfalls-i-hit-these-so-you-dont-have-to">Common pitfalls (I hit these so you dont have to)</h2>
<ul>
<li><strong>Host Nginx vs Docker Nginx:</strong> If you already run Nginx on the host, dont also bind 80/443/8448 in a Docker Nginx — youll get <code>bind() ... already in use</code> and restart loops. Use <strong>host</strong> Nginx to reverse proxy to containers.</li>
<li><strong>Nginx <code>http2</code> directive:</strong> Old Nginx may not support the <code>http2</code> directive on <code>listen</code>. Use <code>listen 443 ssl;</code> (and add <code>http2</code> if your version supports it).</li>
<li><strong>Certificate name mismatch:</strong> Make sure <code>rtc.example.com</code>s vhost uses a certificate <strong>for that exact hostname</strong> (initially I had the <code>matrix.*</code> cert on <code>rtc.*</code> and curl complained).</li>
<li><strong>Postgres collation:</strong> Either initialize the cluster with <code>C</code> or use <code>allow_unsafe_locale: true</code> in Synapse DB config to get running quickly.</li>
<li><strong>CORS duplication on <code>/sfu/get</code>:</strong> Only ONE <code>Access-Control-Allow-Origin</code> header. If the upstream adds it too, use <code>proxy_hide_header Access-Control-Allow-Origin;</code> on the Nginx location.</li>
<li><strong>Cloudflare:</strong> Use <strong>DNS-only</strong> for <code>rtc.*</code>. Proxies can interfere with WS and UDP paths.</li>
<li><strong>Firewall:</strong> Open the LiveKit UDP range and TURN ports on both v4 and v6.</li>
</ul>
<hr>
<h2 id="final-checklist-print-me">Final checklist (print me)</h2>
<ul>
<li><input disabled="" type="checkbox"> <code>https://matrix.example.com/.well-known/matrix/client</code> returns <strong>both</strong> <code>m.homeserver.base_url</code> and <code>org.matrix.msc4143.rtc_foci</code> pointing to <code>https://rtc.example.com</code>.</li>
<li><input disabled="" type="checkbox"> <code>/_matrix/client/versions</code> shows <code>&quot;org.matrix.msc4140&quot;: true</code>.</li>
<li><input disabled="" type="checkbox"> <code>/sfu/get</code> <strong>preflight</strong> returns <strong>one</strong> <code>Access-Control-Allow-Origin</code> and <strong>200/204</strong>.</li>
<li><input disabled="" type="checkbox"> Starting a call creates <strong>HTTP 101</strong> entries to <code>wss://rtc.example.com</code> in <code>rtc.access.log</code>.</li>
<li><input disabled="" type="checkbox"> LiveKit logs show <strong>participants joining</strong> (not just CreateRoom).</li>
<li><input disabled="" type="checkbox"> <code>/voip/turnServer</code> returns time-limited TURN credentials.</li>
<li><input disabled="" type="checkbox"> Cloudflare set to <strong>DNS-only</strong> for <code>rtc.*</code>. UFW allows 7881/tcp and your LiveKit UDP range.</li>
</ul>
<hr>
<h3 id="credits--tooling">Credits &amp; tooling</h3>
<ul>
<li>Matrix Synapse, coTURN, LiveKit, Element Call.</li>
<li><code>curl</code>, <code>jq</code>, <code>docker compose logs</code>, Nginx access logs. These are your best friends.</li>
<li>The debugging breakthrough was catching CORS errors in the browser console and looking for <strong>HTTP 101</strong> in Nginx logs.</li>
</ul>
<p>Happy calling! 🎉</p>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/element-call/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/element-call/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/element-call/"></head></html>

View file

@ -0,0 +1,12 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Fabrication | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/fabrication/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/fabrication/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/fabrication/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/fabrication/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Fabrication"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Fabrication"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/>Tags</a></div><h1>Fabrication
<a href=/tags/fabrication/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><figure class=entry-cover><img loading=lazy src=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/images/inet/inet_animation_collage.jpg alt="Six looks of the INET LED logo"></figure><header class=entry-header><h2 class=entry-hint-parent>INET Logo That Breathes — From CAD to LEDs to a Calm Little Server</h2></header><div class=entry-content><p>I didnt add a warning sign to the room. I taught the logo to breathe. ;-D
The Rotunde is a good room for bold ideas and yummy coffee. The door sighs shut and the outside world gives up on us. The only thing its not good at is ventilating itself. Forty minutes into a group meeting, or a sressful defence, and we all feel like sleeping and running back to our offices!
...</p></div><footer class=entry-footer><span title='2025-10-17 00:00:00 +0000 UTC'>October 17, 2025</span>&nbsp;·&nbsp;16 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/inet_logo/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to INET Logo That Breathes — From CAD to LEDs to a Calm Little Server" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/inet_logo/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,368 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Fabrication on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/fabrication/</link><description>Recent content in Fabrication on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Fri, 17 Oct 2025 00:00:00 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/fabrication/index.xml" rel="self" type="application/rss+xml"/><item><title>INET Logo That Breathes — From CAD to LEDs to a Calm Little Server</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/inet_logo/</link><pubDate>Fri, 17 Oct 2025 00:00:00 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/inet_logo/</guid><description>&lt;blockquote>
&lt;p>I didnt add a warning sign to the room. I taught the &lt;strong>logo&lt;/strong> to breathe. ;-D&lt;/p>&lt;/blockquote>
&lt;p>The Rotunde is a good room for bold ideas and yummy coffee. The door sighs shut and the outside world gives up on us. The only thing it&amp;rsquo;s not good at is &lt;strong>ventilating&lt;/strong> itself. Forty minutes into a group meeting, or a sressful defence, and we all feel like sleeping and running back to our offices!&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>I didnt add a warning sign to the room. I taught the <strong>logo</strong> to breathe. ;-D</p></blockquote>
<p>The Rotunde is a good room for bold ideas and yummy coffee. The door sighs shut and the outside world gives up on us. The only thing it&rsquo;s not good at is <strong>ventilating</strong> itself. Forty minutes into a group meeting, or a sressful defence, and we all feel like sleeping and running back to our offices!</p>
<p>So I crafted the INET logo and gave it a 9-5 job: be a <strong>polite barometer</strong>. If the air is fresh, it glows cool and calm. If its getting stale, it warms up. No blinking warnings, no sound effects — just mood.</p>
<p>This post is the story of how I designed the channel and diffuser, printed the parts, cut, layed out, and soldered the snippets of LED strips, wrote the software, and wrapped it all in a systemd service so it wakes up with the Pi. Feel free to grab a cup of coffee and enjoy the post! I&rsquo;ll try to include as much detail as I can.</p>
<hr>
<h2 id="1-sketch--cad--print">1) Sketch → CAD → Print</h2>
<p><img alt="Fusion design + logotype" loading="lazy" src="/images/inet/inet_logo_fusion_merged.png"></p>
<p>I started the way all sensible hardware projects start: with <strong>overconfidence</strong> and a sketch. The INET logotype looks simple from the front but its a small maze inside. I modeled pockets for each letter in Fusion: a tall <strong>I</strong>, the curving <strong>N</strong>, three stacked bars for <strong>E</strong>, and the long arm of <strong>T</strong>. Each pocket got:</p>
<ul>
<li><strong>Mounting bosses</strong> for the front diffusers (M2 screws),</li>
<li><strong>Cable holes</strong> between chambers (rounded to avoid cutting silicone wire), and</li>
<li>A small <strong>wiring bay</strong> for the controller and power.</li>
</ul>
<p>I printed the channel parts in matte black PLA for heat resilience and the diffusers in white PLA at <strong>1-2 mm</strong> thick to hide hotspots without wasting brightness. That thickness ended up perfect; you can still see motion, but the individual LED package disappears.
The print process took around 24h, including the misprints, and the in-between prints for adjusting the sizes and calibrating everything. Each letter was either printed in multiple parts, or individually so that the logo becomes as large as possible. I used around 1Kg of fillament to print the whole thing.</p>
<p>The design process took around 18 iterations, with initial iteration being simple letters in italic form, and the later ones being more similar to the original INET logo of our group.
I had some experience with CAD, and had designed some simple things to address my everyday problems, but this was a more serious one. Overall it took me a few days to design the logo and print it part by part. The design is not the most artistic, but as authentic as I could get it.</p>
<hr>
<h2 id="2-the-look-i-wanted">2) The Look I Wanted</h2>
<p><img alt="Animations collage" loading="lazy" src="/images/inet/inet_animation_collage.jpg"></p>
<p>I wasn&rsquo;t building a stage light. The goal was <strong>calm</strong>: slow waves, breathing brightness, gentle comets. Everything reads through those letter chambers like a tiny light sculpture. The cyan frame in the collage is the <strong>CO₂ monitor</strong> in a “fresh air” state (more on the color language below). There are many many other animations that I stole from different git repositories, but the most used one is the Co2 monitor with breathing animation. I did want to include more animations other than breathing, but that will be a future me problem with creativity left to spare; right now my creativity well is as dry as the paint on my wall! For now, I&rsquo;m happy with it and want to let it be there doing it&rsquo;s job.</p>
<hr>
<h2 id="3-the-tidy-mess-behind-the-panel">3) The Tidy Mess Behind the Panel</h2>
<p><img alt="Hardware collage" loading="lazy" src="/images/inet/inet_hardware_collage.jpg"></p>
<p>Inside the box theres a Raspberry Pi zero 2 W, a short run of WS2812B LEDs (78 pixels total), a 5 V 34 A supply, jumpers that mind their manners, and two little hardware choices that pay rent every day:</p>
<ul>
<li>A <strong>330470 Ω</strong> series resistor on the <em>data</em> line right at the first pixel. It damps ringing and prevents weird flickers.</li>
<li>A <strong>1000 µF</strong> electrolytic across <strong>5V/GND</strong> at the strip start. It handles inrush so the first LED doesnt faint at boot.</li>
</ul>
<p>I route the strip <strong>DIN → DOUT</strong> through the chambers and use short silicone jumpers at the bends. Every joint gets heatshrink and a tiny dab of hot glue as strain relief. I continuitycheck <strong>before</strong> power and bring brightness up slowly on a bench supply.</p>
<p>The soldering was a big mess. I had never done hardware debugging before. My soldering skills were definitly challenged for this project, as the connections kept breaking when I moved the logo. I did learn how to perform better soldering, but it was already too late. I had to tin the wires after adding flux, and let the soldering wire go up the wire to get stronger connections and leave no naked coppers. A note to remember for next soldering journeys. It took me around two whole days to solder everything and debug it. At some point there was a faulty LED in the middle that caused shorting, and I found and cut it out. This was the most annoying part of the debugging as I had to go through the LEDs, measuring voltage difference to see what is causing the problem. I honestly gave up somewhere in here and thought I won&rsquo;t be able to push through the marathon, but here I am, done with the logo, happy as a four young old licking their ice cream! xD</p>
<hr>
<h2 id="4-a-web-panel-that-stays-out-of-the-way">4) A Web Panel that Stays Out of the Way</h2>
<p><img alt="UI collage" loading="lazy" src="/images/inet/inet_ui_collage.jpg"></p>
<p>The web UI is quiet on purpose: a single navbar, a <code>/control</code> page with big samesize buttons, a <code>/schedule</code> table, a draganddrop <code>/calendar</code>, a <code>/status</code> page that updates once a second, and <code>/history</code> charts for CO₂/temperature/humidity with white backgrounds so theyre legible on a projector. Temprature values are not about room, but the box, as I could not mount the sensor outside of the box easily. I just let it sit inside the box. The panel was created with the help of trusty vibe-coding (!). I used chat GPT to create a template, and expanded it for the purpose. Flask made things very easy.</p>
<p>Theres also a <strong>Safe Mode</strong> switch that hides flashier patterns. Meeting rooms are for thinking, not strobe tests, and you never know who might have photosensitive epilepsy, and it&rsquo;s not funny for anyone to fidn this out when looking at your creation. So&hellip; yea, I took precautions not to see people getting seizures looking at my creation. :-)</p>
<hr>
<h2 id="5-the-color-language-for-air">5) The Color Language for Air</h2>
<p>The <code>co2_monitor</code> animation maps CO₂ ppm → color and adds slow motion so it doesnt feel like a stoplight:</p>
<ul>
<li><strong>&lt; 500 ppm</strong>: Cyan — outdoorlike fresh air.</li>
<li><strong>500799</strong>: Green — good.</li>
<li><strong>8001199</strong>: Yellow — getting stale.</li>
<li><strong>12001499</strong>: Orange — poor; youll feel it.</li>
<li><strong>15001999</strong>: Red — ventilate now.</li>
<li><strong>≥ 2000</strong>: Purple — the logo is politely yelling.</li>
</ul>
<p>I blend the readings with an <strong>exponential moving average</strong> and use <strong>hysteresis</strong> around thresholds so it doesnt pingpong colors when the room hovers at 800 ppm. Then I ease the current hue toward the target color and apply a very slow <strong>breathing brightness</strong>. The result feels alive but never flashy.</p>
<p>By default, the lights turn off from 20 to 6, then from 6 to 9, and 5 to 8 the standby red pulse is shown. From 9-5 on workdays the logo does it&rsquo;s daily job of Co2 monitoring. Well, technically it does that all the time, but during those hours it shows the Co2 level by it&rsquo;s color, the remaining time it just keeps a record of the Co2, temprature and humidity of room in the database.</p>
<hr>
<h2 id="6-the-code--a-guided-tour-no-giant-blob-promise">6) The Code — a guided tour (no giant blob, promise)</h2>
<p>This whole project runs from a single Python file. Think of it like a tiny orchestra:<br>
one thread conducts the <strong>LEDs</strong>, one listens politely to the <strong>CO₂ sensor</strong>, one keeps time as the <strong>scheduler</strong>, and a small <strong>web server</strong> hands you the baton when you want to improvise.</p>
<p>Below is what each section does, with just enough code to be useful (and not enough to make your eyes cross).</p>
<hr>
<h3 id="61-configuration-the-knobs">6.1 Configuration (the knobs)</h3>
<p>Let&rsquo;s define where the LEDs live, how bright they can get, and where to save tiny bits of state (schedule and sensor DB). All of it can be overridden with environment variables so you dont edit code to change pin numbers.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>LED_COUNT<span style="color:#f92672">=</span><span style="color:#ae81ff">78</span>, LED_PIN<span style="color:#f92672">=</span><span style="color:#ae81ff">18</span>, LED_BRIGHT<span style="color:#f92672">=</span><span style="color:#ae81ff">255</span>
</span></span><span style="display:flex;"><span>SCHEDULE_PATH<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;schedule.json&#34;</span>
</span></span><span style="display:flex;"><span>DB_PATH<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;sensor.db&#34;</span>
</span></span><span style="display:flex;"><span>SAFE_MODE<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span> <span style="color:#75715e"># hide flashy animations by default</span>
</span></span></code></pre></div><p><em>Why its like this:</em> simple, explicit defaults → less “why is it dark” debugging.</p>
<hr>
<h3 id="62-strip-setup--frame-diff-no-flicker-magic">6.2 Strip setup + frame-diff (no flicker magic)</h3>
<p>Now initialize <code>rpi_ws281x.PixelStrip</code> and put a <strong>shadow frame buffer</strong> in front of it.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>frame <span style="color:#f92672">=</span> [(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>)] <span style="color:#f92672">*</span> LED_COUNT
</span></span><span style="display:flex;"><span>_dirty <span style="color:#f92672">=</span> <span style="color:#66d9ef">False</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">_set_pixel</span>(i, r, g, b):
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># only touch hardware if the value actually changed</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> frame[i] <span style="color:#f92672">!=</span> (r,g,b):
</span></span><span style="display:flex;"><span> strip<span style="color:#f92672">.</span>setPixelColorRGB(i, r, g, b)
</span></span><span style="display:flex;"><span> frame[i] <span style="color:#f92672">=</span> (r,g,b)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">global</span> _dirty; _dirty <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">flush</span>():
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> _dirty: strip<span style="color:#f92672">.</span>show(); _dirty<span style="color:#f92672">=</span><span style="color:#66d9ef">False</span>
</span></span></code></pre></div><p><em>Why its like this:</em> LEDs are zen monks—disturb them only when you must. The frame-diff prevents those mysterious “one frame flash” moments that happen when you call <code>show()</code> with identical data.</p>
<hr>
<h3 id="63-a-tiny-color-wheel-helper">6.3 A tiny color wheel helper</h3>
<p>Classic rainbow helper used by a few animations:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">wheel</span>(pos):
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># 0..255 → (r,g,b)</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">...</span>
</span></span></code></pre></div><p><em>Why its like this:</em> I&rsquo;ll use it again and again; it keeps color math out of the animations.</p>
<hr>
<h3 id="64-state--orchestration">6.4 State &amp; orchestration</h3>
<p>Let&rsquo;s hold a registry of animations, a stop flag, and the currently playing thread.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>animations <span style="color:#f92672">=</span> {}
</span></span><span style="display:flex;"><span>stop_event <span style="color:#f92672">=</span> threading<span style="color:#f92672">.</span>Event()
</span></span><span style="display:flex;"><span>current_thread <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">register</span>(name, safe<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>):
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">wrap</span>(fn):
</span></span><span style="display:flex;"><span> animations[name] <span style="color:#f92672">=</span> {<span style="color:#e6db74">&#34;fn&#34;</span>: fn, <span style="color:#e6db74">&#34;safe&#34;</span>: safe}
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> fn
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> wrap
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">run_animation</span>(fn):
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># cooperative hand-off: stop current, start next</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> current_thread <span style="color:#f92672">and</span> current_thread<span style="color:#f92672">.</span>is_alive():
</span></span><span style="display:flex;"><span> stop_event<span style="color:#f92672">.</span>set(); current_thread<span style="color:#f92672">.</span>join()
</span></span><span style="display:flex;"><span> stop_event<span style="color:#f92672">.</span>clear()
</span></span><span style="display:flex;"><span> t <span style="color:#f92672">=</span> threading<span style="color:#f92672">.</span>Thread(target<span style="color:#f92672">=</span>fn, name<span style="color:#f92672">=</span>fn<span style="color:#f92672">.</span>__name__, daemon<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>)
</span></span><span style="display:flex;"><span> t<span style="color:#f92672">.</span>start(); globals()[<span style="color:#e6db74">&#34;current_thread&#34;</span>] <span style="color:#f92672">=</span> t
</span></span></code></pre></div><p><em>Why its like this:</em> adding a new animation is a one-liner <code>@register(&quot;name&quot;)</code>. Clean exits prevent torn frames and half-drawn comets.</p>
<hr>
<h3 id="65-animations-the-mood">6.5 Animations (the mood)</h3>
<p>Each animation is a loop that checks <code>stop_event.is_set()</code> and draws frames with <code>_set_pixel(...)</code> + <code>flush()</code>.</p>
<ul>
<li><strong><code>redpulse</code></strong> — calm breathing in red; great default.</li>
<li><strong><code>colorwave</code></strong> — slow rainbow that reads clearly behind diffusers.</li>
<li><strong><code>comet</code></strong> — a single head with a fading tail orbiting the logo.</li>
</ul>
<p>They all end with <code>finally: clear_strip()</code> so the panel doesnt freeze on the last pose if you stop mid-frame.</p>
<p><em>Why its like this:</em> cooperative loops + frame-diff = smooth, interruption-safe effects.</p>
<hr>
<h3 id="66-co-color-language-with-smoothing--hysteresis">6.6 CO₂ color language (with smoothing + hysteresis)</h3>
<p>The star of the show. Now map ppm to color <strong>bands</strong> and keep transitions human-pleasant.</p>
<ul>
<li><code>&lt; 500</code>: <strong>Cyan</strong> (fresh)</li>
<li><code>500799</code>: <strong>Green</strong></li>
<li><code>8001199</code>: <strong>Yellow</strong></li>
<li><code>12001499</code>: <strong>Orange</strong></li>
<li><code>15001999</code>: <strong>Red</strong></li>
<li><code>≥ 2000</code>: <strong>Purple</strong></li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>BANDS<span style="color:#f92672">=</span>[(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">500</span>,(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">214</span>,<span style="color:#ae81ff">255</span>)), (<span style="color:#ae81ff">500</span>,<span style="color:#ae81ff">800</span>,(<span style="color:#ae81ff">17</span>,<span style="color:#ae81ff">204</span>,<span style="color:#ae81ff">85</span>)), <span style="color:#f92672">...</span> (<span style="color:#ae81ff">2000</span>,<span style="color:#960050;background-color:#1e0010"></span>,(<span style="color:#ae81ff">123</span>,<span style="color:#ae81ff">44</span>,<span style="color:#ae81ff">191</span>))]
</span></span><span style="display:flex;"><span>EMA_ALPHA<span style="color:#f92672">=</span><span style="color:#ae81ff">0.15</span>; HYST<span style="color:#f92672">=</span><span style="color:#ae81ff">35</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Every tick:</span>
</span></span><span style="display:flex;"><span>ema <span style="color:#f92672">=</span> (<span style="color:#ae81ff">1</span><span style="color:#f92672">-</span>EMA_ALPHA)<span style="color:#f92672">*</span>ema <span style="color:#f92672">+</span> EMA_ALPHA<span style="color:#f92672">*</span>co2
</span></span><span style="display:flex;"><span>band <span style="color:#f92672">=</span> hysteresis_aware_band(ema, last_band, HYST)
</span></span><span style="display:flex;"><span>target <span style="color:#f92672">=</span> BANDS[band]<span style="color:#f92672">.</span>color
</span></span><span style="display:flex;"><span>current <span style="color:#f92672">=</span> lerp(current, target, <span style="color:#ae81ff">0.10</span>) <span style="color:#75715e"># gentle hue easing</span>
</span></span><span style="display:flex;"><span>level <span style="color:#f92672">=</span> breathe(<span style="color:#ae81ff">0.25</span> Hz, low<span style="color:#f92672">=</span><span style="color:#ae81ff">10</span>, high<span style="color:#f92672">=</span><span style="color:#ae81ff">220</span>)
</span></span><span style="display:flex;"><span>draw all pixels <span style="color:#f92672">=</span> current <span style="color:#f92672">*</span> level
</span></span></code></pre></div><p><em>Why its like this:</em> EMA + hysteresis prevents “800↔801 disco.” The slow breathing keeps the panel feeling alive, not like a traffic light.</p>
<hr>
<h3 id="67-optional-scd41-sensor-thread-the-polite-listener">6.7 Optional SCD41 sensor thread (the polite listener)</h3>
<p>If the SCD41 is present, a <strong>dedicated thread</strong> owns I²C and updates a global <code>co2_data</code> dict every few seconds. It also logs to a tiny SQLite database.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">_start_sensor</span>():
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">try</span>:
</span></span><span style="display:flex;"><span> scd4x <span style="color:#f92672">=</span> adafruit_scd4x<span style="color:#f92672">.</span>SCD4X(i2c); scd4x<span style="color:#f92672">.</span>start_periodic_measurement()
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">except</span>: <span style="color:#66d9ef">return</span> <span style="color:#75715e"># sensor optional</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">loop</span>():
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">while</span> <span style="color:#66d9ef">True</span>:
</span></span><span style="display:flex;"><span> time<span style="color:#f92672">.</span>sleep(<span style="color:#ae81ff">5</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> scd4x<span style="color:#f92672">.</span>data_ready:
</span></span><span style="display:flex;"><span> co2_data<span style="color:#f92672">.</span>update({<span style="color:#f92672">...</span>})
</span></span><span style="display:flex;"><span> db<span style="color:#f92672">.</span>insert(timestamp, co2, temperature, humidity)
</span></span><span style="display:flex;"><span> threading<span style="color:#f92672">.</span>Thread(target<span style="color:#f92672">=</span>loop, daemon<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>)<span style="color:#f92672">.</span>start()
</span></span></code></pre></div><p><em>Why its like this:</em> one thread owns the bus → no read contention. The controller keeps working even without a sensor.</p>
<hr>
<h3 id="68-scheduler--90-minute-override-humans-win-then-reset">6.8 Scheduler + 90-minute override (humans win, then reset)</h3>
<p>A background thread asks, every few seconds, <strong>which</strong> mode should be running:</p>
<ol>
<li>If the user chose something recently (override), respect it for <code>TTL = 90 min</code> (or the duration set in the UI).</li>
<li>Otherwise, apply the <strong>default schedule</strong> (Wednesday 1417 → <code>slow</code>, else <code>redpulse</code>).</li>
<li>Save/load the schedule atomically to <code>schedule.json</code>.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">scheduler_pick</span>():
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> now <span style="color:#f92672">&lt;</span> override_until: <span style="color:#66d9ef">return</span> override_mode
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> row <span style="color:#f92672">in</span> load_schedule():
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> matches(now, row): <span style="color:#66d9ef">return</span> row[<span style="color:#e6db74">&#34;mode&#34;</span>]
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;redpulse&#34;</span>
</span></span></code></pre></div><p><em>Why its like this:</em> you can play DJ for a meeting, and the room quietly returns to its routine afterward.</p>
<hr>
<h3 id="69-the-web-panel-tiny-flask-big-buttons">6.9 The web panel (tiny Flask, big buttons)</h3>
<p>Three pages, no fuss:</p>
<ul>
<li><code>/status</code> — live JSON (<code>/status_data</code>) every second → current mode + CO₂/Temp/Humidity.</li>
<li><code>/control</code> — grid of same-size buttons (respects <strong>Safe Mode</strong>), optional <strong>duration</strong> (0180 min) with validation.</li>
<li><code>/schedule</code> — simple table editor; <strong>Add Row</strong> and <strong>Save</strong>; writes atomically.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#a6e22e">@app.post</span>(<span style="color:#e6db74">&#34;/set&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">set_mode</span>():
</span></span><span style="display:flex;"><span> mode <span style="color:#f92672">=</span> request<span style="color:#f92672">.</span>form[<span style="color:#e6db74">&#34;mode&#34;</span>]
</span></span><span style="display:flex;"><span> minutes <span style="color:#f92672">=</span> clamp( int(form[<span style="color:#e6db74">&#34;duration&#34;</span>]), <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">180</span> )
</span></span><span style="display:flex;"><span> set_override(mode, minutes)
</span></span><span style="display:flex;"><span> run_animation(animations[mode][<span style="color:#e6db74">&#34;fn&#34;</span>])
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> redirect(<span style="color:#e6db74">&#34;/control&#34;</span>)
</span></span></code></pre></div><p><em>Why its like this:</em> minimal routes are easier to maintain and dont compete with the art piece.</p>
<hr>
<h3 id="610-boot-choreography">6.10 Boot choreography</h3>
<p>At startup I:</p>
<ol>
<li>Try the <strong>sensor thread</strong> (if hardware is there).</li>
<li>Start the <strong>scheduler thread</strong>.</li>
<li>Immediately play <strong>redpulse</strong> (so theres a friendly glow right away).</li>
<li>Start Flask; on shutdown I <strong>clear the strip</strong>.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> __name__ <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;__main__&#34;</span>:
</span></span><span style="display:flex;"><span> _start_sensor()
</span></span><span style="display:flex;"><span> threading<span style="color:#f92672">.</span>Thread(target<span style="color:#f92672">=</span>scheduler_loop, daemon<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>)<span style="color:#f92672">.</span>start()
</span></span><span style="display:flex;"><span> run_animation(redpulse)
</span></span><span style="display:flex;"><span> app<span style="color:#f92672">.</span>run(host<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;0.0.0.0&#34;</span>, port<span style="color:#f92672">=</span><span style="color:#ae81ff">5000</span>)
</span></span></code></pre></div><p><em>Why its like this:</em> the room sees something alive instantly; the scheduler will swap in the “real” choice within a few seconds.</p>
<hr>
<h3 id="tldr-but-keep-the-vibes">TL;DR (but keep the vibes)</h3>
<ul>
<li><strong>Frame-diff</strong> = no flashes.</li>
<li><strong>EMA + hysteresis</strong> = smooth, truthful color.</li>
<li><strong>Breathing</strong> = presence, not signage.</li>
<li><strong>Threads per thing</strong> (LEDs / sensor / scheduler) = no fights.</li>
<li><strong>Atomic files</strong> = no corrupted schedules.</li>
<li><strong>Safe Mode</strong> by default = people &gt; pixels.</li>
</ul>
<p>Thats it — the code behaves like a considerate colleague: dependable, quiet, and occasionally very pretty.</p>
<h3 id="why-this-shape-of-code">Why this shape of code?</h3>
<ul>
<li><strong>One thread per hardware thing.</strong> The sensor thread owns I²C. The animation thread owns LEDs. The scheduler just decides <em>what</em> to run and when. No bus fights.</li>
<li><strong>Frame diff.</strong> I only call <code>strip.show()</code> when a pixel actually changes. Thats why it doesnt randomly flash during web requests.</li>
<li><strong>Atomic schedule saves.</strong> The code writes to a temporary file and replaces the old one so a midsave power cut cant corrupt the schedule.</li>
</ul>
<blockquote>
<p>No, you dont <em>need</em> the sensor. If its not connected, the app still runs; <code>co2_monitor</code> just sits at the default value. Well, technically it shows NaN as the numbers! But it is fun to have a sensor, don&rsquo;t you agree?</p></blockquote>
<hr>
<h2 id="7-sensor-database--what-it-stores-where-it-lives-and-how-far-it-goes">7) Sensor Database — what it stores, where it lives, and how far it goes</h2>
<p>This project logs room conditions to a <strong>tiny SQLite database</strong> so you can graph trends, spot stuffy meetings, and keep a record without running a separate server.</p>
<h3 id="whats-stored">Whats stored</h3>
<p>A single table called <code>readings</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">CREATE</span> <span style="color:#66d9ef">TABLE</span> <span style="color:#66d9ef">IF</span> <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">EXISTS</span> readings (
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">timestamp</span> TEXT <span style="color:#66d9ef">PRIMARY</span> <span style="color:#66d9ef">KEY</span>, <span style="color:#75715e">-- &#34;YYYY-MM-DD HH:MM:SS&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> co2 INTEGER, <span style="color:#75715e">-- ppm
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> temperature REAL, <span style="color:#75715e">-- °C
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> humidity REAL <span style="color:#75715e">-- %
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>);
</span></span></code></pre></div><ul>
<li>One row per sample (typically every <strong>560 seconds</strong>; slow it down to reduce writes).</li>
<li><code>timestamp</code> is the primary key → easy “latest” queries and natural time ordering.</li>
</ul>
<h3 id="where-the-file-lives">Where the file lives</h3>
<ul>
<li>Path is configurable via env var <code>DB_PATH</code> (see your systemd unit).</li>
<li>Default: <code>sensor.db</code> in the apps working directory (e.g. <code>/home/pi/inet-led/sensor.db</code>).</li>
</ul>
<p>Check size:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ls -lh /home/pi/inet-led/sensor.db
</span></span></code></pre></div><h3 id="how-writes-happen-and-why-its-safe">How writes happen (and why its safe)</h3>
<ul>
<li>The <strong>sensor thread</strong> owns I²C and inserts a row only when the sensor reports <code>data_ready</code>.</li>
<li>Inserts are short and journaling protects against power loss.</li>
<li>For friendlier read/write concurrency, enable WAL once at startup:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span>PRAGMA journal_mode <span style="color:#f92672">=</span> WAL;
</span></span><span style="display:flex;"><span>PRAGMA synchronous <span style="color:#f92672">=</span> NORMAL;
</span></span></code></pre></div><h3 id="typical-size--retention">Typical size &amp; retention</h3>
<p>Back-of-envelope:</p>
<ul>
<li>~100200 bytes per row (including overhead).</li>
<li>1 sample/minute → ~1,440 rows/day → <strong>~150300 KB/day</strong><strong>55110 MB/year</strong>.</li>
<li>If you log every 5 seconds, multiply by ~12 (consider slower logging or downsampling).</li>
</ul>
<p>Prune old data (keep last 90 days):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">DELETE</span> <span style="color:#66d9ef">FROM</span> readings
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span> <span style="color:#66d9ef">timestamp</span> <span style="color:#f92672">&lt;</span> date(<span style="color:#e6db74">&#39;now&#39;</span>,<span style="color:#e6db74">&#39;-90 day&#39;</span>);
</span></span></code></pre></div><p>(Optionally run <code>VACUUM;</code> after large deletes to reclaim file space.)</p>
<h3 id="useful-queries">Useful queries</h3>
<p><strong>Latest reading:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">FROM</span> readings <span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> <span style="color:#66d9ef">timestamp</span> <span style="color:#66d9ef">DESC</span> <span style="color:#66d9ef">LIMIT</span> <span style="color:#ae81ff">1</span>;
</span></span></code></pre></div><p><strong>Range for charts (last 7 days):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">FROM</span> readings
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span> <span style="color:#66d9ef">timestamp</span> <span style="color:#f92672">&gt;=</span> datetime(<span style="color:#e6db74">&#39;now&#39;</span>,<span style="color:#e6db74">&#39;-7 day&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> <span style="color:#66d9ef">timestamp</span>;
</span></span></code></pre></div><p><strong>Downsample to hourly averages (30 days):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> strftime(<span style="color:#e6db74">&#39;%Y-%m-%d %H:00:00&#39;</span>, <span style="color:#66d9ef">timestamp</span>) <span style="color:#66d9ef">AS</span> hour,
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">AVG</span>(co2) <span style="color:#66d9ef">AS</span> co2, <span style="color:#66d9ef">AVG</span>(temperature) <span style="color:#66d9ef">AS</span> t, <span style="color:#66d9ef">AVG</span>(humidity) <span style="color:#66d9ef">AS</span> h
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> readings
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span> <span style="color:#66d9ef">timestamp</span> <span style="color:#f92672">&gt;=</span> datetime(<span style="color:#e6db74">&#39;now&#39;</span>,<span style="color:#e6db74">&#39;-30 day&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">GROUP</span> <span style="color:#66d9ef">BY</span> hour
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> hour;
</span></span></code></pre></div><p><strong>Export to CSV (example via sqlite3 CLI):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sqlite3 /home/pi/inet-led/sensor.db <span style="color:#e6db74">&lt;&lt;&#39;SQL&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">.headers on
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">.mode csv
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">.output /home/pi/inet-led/export_readings.csv
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">SELECT * FROM readings ORDER BY timestamp;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">.output stdout
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">SQL</span>
</span></span></code></pre></div><h3 id="sd-card-friendliness">SD card friendliness</h3>
<ul>
<li>Choose a sensible interval (e.g., <strong>3060 s</strong>).</li>
<li>Optionally buffer in memory and write every N samples.</li>
<li>Prefer WAL mode; periodically prune &amp; vacuum.</li>
<li>If you care about card wear, place the DB on USB/SSD.</li>
</ul>
<h3 id="backups--restore">Backups &amp; restore</h3>
<p><strong>Nightly backup (simple):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sqlite3 /home/pi/inet-led/sensor.db <span style="color:#e6db74">&#34;.backup &#39;/home/pi/backup/sensor-</span><span style="color:#66d9ef">$(</span>date +%F<span style="color:#66d9ef">)</span><span style="color:#e6db74">.db&#39;&#34;</span>
</span></span></code></pre></div><p><strong>Restore:</strong></p>
<ol>
<li><code>sudo systemctl stop inet-led</code></li>
<li>Copy backup file back to <code>/home/pi/inet-led/sensor.db</code></li>
<li><code>sudo systemctl start inet-led</code></li>
</ol>
<h3 id="security--permissions">Security &amp; permissions</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>chown pi:pi /home/pi/inet-led/sensor.db
</span></span><span style="display:flex;"><span>chmod <span style="color:#ae81ff">600</span> /home/pi/inet-led/sensor.db
</span></span></code></pre></div><p>Keep the app directory non-world-readable.</p>
<h3 id="is-sqlite-the-right-choice">Is SQLite the right choice?</h3>
<p><strong>Yes, for a single Pi</strong> logging every few seconds and rendering local charts:</p>
<ul>
<li>✅ Zero admin, a single file, reliable journaling, great performance at this scale.</li>
<li>⚠️ One writer at a time (I only have the sensor thread writing, so its fine).</li>
<li>⬆️ If you later need multi-device ingestion, alerts, or &gt;10s of millions of rows, consider a time-series DB (TimescaleDB/InfluxDB/VictoriaMetrics) and migrate using CSV exports.</li>
</ul>
<h3 id="tldr">TL;DR</h3>
<p>SQLite is perfect here: <strong>simple, robust, easy to back up</strong>. Start with it, and only upgrade when your ambitions outgrow a single Raspberry Pi.</p>
<hr>
<h2 id="8-run-at-boot-systemd">8) Run at Boot (systemd)</h2>
<p>I wrote this simple systemd to <code>/etc/systemd/system/inet-led.service</code> so that it runs the script when RPi boots up:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#66d9ef">[Unit]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Description</span><span style="color:#f92672">=</span><span style="color:#e6db74">INET LED Panel</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">After</span><span style="color:#f92672">=</span><span style="color:#e6db74">network.target</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">[Service]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ExecStart</span><span style="color:#f92672">=</span><span style="color:#e6db74">/usr/bin/python3 /home/pi/inet-led/inet_led_panel.py</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">WorkingDirectory</span><span style="color:#f92672">=</span><span style="color:#e6db74">/home/pi/inet-led</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Restart</span><span style="color:#f92672">=</span><span style="color:#e6db74">always</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">User</span><span style="color:#f92672">=</span><span style="color:#e6db74">pi</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Environment</span><span style="color:#f92672">=</span><span style="color:#e6db74">LED_COUNT=78</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Environment</span><span style="color:#f92672">=</span><span style="color:#e6db74">SCHEDULE_FILE=/home/pi/inet-led/schedule.json</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Environment</span><span style="color:#f92672">=</span><span style="color:#e6db74">DB_PATH=/home/pi/inet-led/sensor.db</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">[Install]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">WantedBy</span><span style="color:#f92672">=</span><span style="color:#e6db74">multi-user.target</span>
</span></span></code></pre></div><p>Then you can run:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo systemctl daemon-reload
</span></span><span style="display:flex;"><span>sudo systemctl enable --now inet-led
</span></span><span style="display:flex;"><span>journalctl -u inet-led -f
</span></span></code></pre></div><p>to control the systemd service.</p>
<hr>
<h2 id="9-soldering-notes-a-love-letter-to-hot-glue">9) Soldering Notes (a love letter to hot glue)</h2>
<ul>
<li><strong>Tin first, solder second.</strong> Tiny pads like tiny puddles. Fast in/out, no lifted pads.</li>
<li><strong>Stagger joints</strong> so nothing stacks under the diffuser.</li>
<li><strong>Heatshrink + a dot of hot glue</strong> = happier future you.</li>
<li><strong>Continuity before power.</strong> Multimeter first, electrons last.</li>
<li>If a pad <em>does</em> lift: magnet wire to a trace, UV mask to seal. Ugly heroics, but it works.</li>
</ul>
<p>Besides the soldering part, I also used m2 screws to fix the diffusers. Initially magnets were suggested, but I felt like figuring that out might take me long, and screws just feel more maintainable&hellip; So&hellip; yea.</p>
<hr>
<h2 id="10-why-these-design-choices">10) Why these design choices?</h2>
<ul>
<li><strong>Calm motion, not flashy.</strong> Meeting rooms breed fatigue; the light should be a helper, not a distraction.</li>
<li><strong>Cyan→Purple language.</strong> Easy to learn, visible at a glance, meaningful without numbers.</li>
<li><strong>White charts, dark UI.</strong> Data should be legible on a projector; buttons shouldnt shout.</li>
<li><strong>Safe Mode default.</strong> People first. Demos are optin.</li>
<li><strong>PLA body, PLA diffuser.</strong> Fast and easy rapid prototyping, easy and fast printing.</li>
</ul>
<hr>
<h2 id="11-what-i-learned">11) What I learned</h2>
<ul>
<li>A logo is a better messenger than a dashboard.</li>
<li>Everyone knows what <strong>orange</strong> means without a legend.</li>
<li>If you show the room a mirror, it corrects itself. Someone will open the door long before you have to ask, and if someone notices the orange/red color of the logo, they would hint the end of the gathering!</li>
<li>Sometimes debugging is not fun at all, and you want to bang your head to the wall, but try not to! Life is short. ^^</li>
</ul>
<hr>
<h2 id="12-final-notes">12) Final notes</h2>
<p>I did this project as a fun distraction and &ldquo;not work&rdquo; as my advisor tells me to do all the time. My advisor provided the LED strip, RPi zero 2 W, and the voltage divider. I got the sensor, designed and coded everything else, and had a lot of fun doing so. I&rsquo;m so so thankful of my advisor for this cool idea, and all the support. It&rsquo;s not the first time I&rsquo;ve been gifted such toys, and as I have recieved other things after that, I know it&rsquo;s not the last.</p>
<p>There is a bug I never was able to figure out, sometimes when I stop the script, I get segmentation fault. I know it is not directly my code with extensive testing, and that the problem is with the library, but I never understood why it happens. Let&rsquo;s hope that doesn&rsquo;t turn into a backdoor into my logo. * Shakingly crosses fingers and sighs in distress!*</p>
<p>The whole project took around 8 days, 4 of which were part of a long weekend. I did do some &ldquo;not work&rdquo; as Tobias always tells me to do, and honestly it was fun and refreshing! I really enjoyed it. I don&rsquo;t know how the group feels/thinks about the logo, but I love it! I hope it won&rsquo;t die after I leave, but even if so, so be it. I had my fun with it. :)</p>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/fabrication/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/fabrication/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/fabrication/"></head></html>

View file

@ -0,0 +1,12 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Flask | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/flask/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/flask/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/flask/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/flask/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Flask"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Flask"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/>Tags</a></div><h1>Flask
<a href=/tags/flask/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><figure class=entry-cover><img loading=lazy src=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/images/inet/inet_animation_collage.jpg alt="Six looks of the INET LED logo"></figure><header class=entry-header><h2 class=entry-hint-parent>INET Logo That Breathes — From CAD to LEDs to a Calm Little Server</h2></header><div class=entry-content><p>I didnt add a warning sign to the room. I taught the logo to breathe. ;-D
The Rotunde is a good room for bold ideas and yummy coffee. The door sighs shut and the outside world gives up on us. The only thing its not good at is ventilating itself. Forty minutes into a group meeting, or a sressful defence, and we all feel like sleeping and running back to our offices!
...</p></div><footer class=entry-footer><span title='2025-10-17 00:00:00 +0000 UTC'>October 17, 2025</span>&nbsp;·&nbsp;16 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/inet_logo/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to INET Logo That Breathes — From CAD to LEDs to a Calm Little Server" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/inet_logo/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

368
public/tags/flask/index.xml Normal file
View file

@ -0,0 +1,368 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Flask on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/flask/</link><description>Recent content in Flask on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Fri, 17 Oct 2025 00:00:00 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/flask/index.xml" rel="self" type="application/rss+xml"/><item><title>INET Logo That Breathes — From CAD to LEDs to a Calm Little Server</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/inet_logo/</link><pubDate>Fri, 17 Oct 2025 00:00:00 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/inet_logo/</guid><description>&lt;blockquote>
&lt;p>I didnt add a warning sign to the room. I taught the &lt;strong>logo&lt;/strong> to breathe. ;-D&lt;/p>&lt;/blockquote>
&lt;p>The Rotunde is a good room for bold ideas and yummy coffee. The door sighs shut and the outside world gives up on us. The only thing it&amp;rsquo;s not good at is &lt;strong>ventilating&lt;/strong> itself. Forty minutes into a group meeting, or a sressful defence, and we all feel like sleeping and running back to our offices!&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>I didnt add a warning sign to the room. I taught the <strong>logo</strong> to breathe. ;-D</p></blockquote>
<p>The Rotunde is a good room for bold ideas and yummy coffee. The door sighs shut and the outside world gives up on us. The only thing it&rsquo;s not good at is <strong>ventilating</strong> itself. Forty minutes into a group meeting, or a sressful defence, and we all feel like sleeping and running back to our offices!</p>
<p>So I crafted the INET logo and gave it a 9-5 job: be a <strong>polite barometer</strong>. If the air is fresh, it glows cool and calm. If its getting stale, it warms up. No blinking warnings, no sound effects — just mood.</p>
<p>This post is the story of how I designed the channel and diffuser, printed the parts, cut, layed out, and soldered the snippets of LED strips, wrote the software, and wrapped it all in a systemd service so it wakes up with the Pi. Feel free to grab a cup of coffee and enjoy the post! I&rsquo;ll try to include as much detail as I can.</p>
<hr>
<h2 id="1-sketch--cad--print">1) Sketch → CAD → Print</h2>
<p><img alt="Fusion design + logotype" loading="lazy" src="/images/inet/inet_logo_fusion_merged.png"></p>
<p>I started the way all sensible hardware projects start: with <strong>overconfidence</strong> and a sketch. The INET logotype looks simple from the front but its a small maze inside. I modeled pockets for each letter in Fusion: a tall <strong>I</strong>, the curving <strong>N</strong>, three stacked bars for <strong>E</strong>, and the long arm of <strong>T</strong>. Each pocket got:</p>
<ul>
<li><strong>Mounting bosses</strong> for the front diffusers (M2 screws),</li>
<li><strong>Cable holes</strong> between chambers (rounded to avoid cutting silicone wire), and</li>
<li>A small <strong>wiring bay</strong> for the controller and power.</li>
</ul>
<p>I printed the channel parts in matte black PLA for heat resilience and the diffusers in white PLA at <strong>1-2 mm</strong> thick to hide hotspots without wasting brightness. That thickness ended up perfect; you can still see motion, but the individual LED package disappears.
The print process took around 24h, including the misprints, and the in-between prints for adjusting the sizes and calibrating everything. Each letter was either printed in multiple parts, or individually so that the logo becomes as large as possible. I used around 1Kg of fillament to print the whole thing.</p>
<p>The design process took around 18 iterations, with initial iteration being simple letters in italic form, and the later ones being more similar to the original INET logo of our group.
I had some experience with CAD, and had designed some simple things to address my everyday problems, but this was a more serious one. Overall it took me a few days to design the logo and print it part by part. The design is not the most artistic, but as authentic as I could get it.</p>
<hr>
<h2 id="2-the-look-i-wanted">2) The Look I Wanted</h2>
<p><img alt="Animations collage" loading="lazy" src="/images/inet/inet_animation_collage.jpg"></p>
<p>I wasn&rsquo;t building a stage light. The goal was <strong>calm</strong>: slow waves, breathing brightness, gentle comets. Everything reads through those letter chambers like a tiny light sculpture. The cyan frame in the collage is the <strong>CO₂ monitor</strong> in a “fresh air” state (more on the color language below). There are many many other animations that I stole from different git repositories, but the most used one is the Co2 monitor with breathing animation. I did want to include more animations other than breathing, but that will be a future me problem with creativity left to spare; right now my creativity well is as dry as the paint on my wall! For now, I&rsquo;m happy with it and want to let it be there doing it&rsquo;s job.</p>
<hr>
<h2 id="3-the-tidy-mess-behind-the-panel">3) The Tidy Mess Behind the Panel</h2>
<p><img alt="Hardware collage" loading="lazy" src="/images/inet/inet_hardware_collage.jpg"></p>
<p>Inside the box theres a Raspberry Pi zero 2 W, a short run of WS2812B LEDs (78 pixels total), a 5 V 34 A supply, jumpers that mind their manners, and two little hardware choices that pay rent every day:</p>
<ul>
<li>A <strong>330470 Ω</strong> series resistor on the <em>data</em> line right at the first pixel. It damps ringing and prevents weird flickers.</li>
<li>A <strong>1000 µF</strong> electrolytic across <strong>5V/GND</strong> at the strip start. It handles inrush so the first LED doesnt faint at boot.</li>
</ul>
<p>I route the strip <strong>DIN → DOUT</strong> through the chambers and use short silicone jumpers at the bends. Every joint gets heatshrink and a tiny dab of hot glue as strain relief. I continuitycheck <strong>before</strong> power and bring brightness up slowly on a bench supply.</p>
<p>The soldering was a big mess. I had never done hardware debugging before. My soldering skills were definitly challenged for this project, as the connections kept breaking when I moved the logo. I did learn how to perform better soldering, but it was already too late. I had to tin the wires after adding flux, and let the soldering wire go up the wire to get stronger connections and leave no naked coppers. A note to remember for next soldering journeys. It took me around two whole days to solder everything and debug it. At some point there was a faulty LED in the middle that caused shorting, and I found and cut it out. This was the most annoying part of the debugging as I had to go through the LEDs, measuring voltage difference to see what is causing the problem. I honestly gave up somewhere in here and thought I won&rsquo;t be able to push through the marathon, but here I am, done with the logo, happy as a four young old licking their ice cream! xD</p>
<hr>
<h2 id="4-a-web-panel-that-stays-out-of-the-way">4) A Web Panel that Stays Out of the Way</h2>
<p><img alt="UI collage" loading="lazy" src="/images/inet/inet_ui_collage.jpg"></p>
<p>The web UI is quiet on purpose: a single navbar, a <code>/control</code> page with big samesize buttons, a <code>/schedule</code> table, a draganddrop <code>/calendar</code>, a <code>/status</code> page that updates once a second, and <code>/history</code> charts for CO₂/temperature/humidity with white backgrounds so theyre legible on a projector. Temprature values are not about room, but the box, as I could not mount the sensor outside of the box easily. I just let it sit inside the box. The panel was created with the help of trusty vibe-coding (!). I used chat GPT to create a template, and expanded it for the purpose. Flask made things very easy.</p>
<p>Theres also a <strong>Safe Mode</strong> switch that hides flashier patterns. Meeting rooms are for thinking, not strobe tests, and you never know who might have photosensitive epilepsy, and it&rsquo;s not funny for anyone to fidn this out when looking at your creation. So&hellip; yea, I took precautions not to see people getting seizures looking at my creation. :-)</p>
<hr>
<h2 id="5-the-color-language-for-air">5) The Color Language for Air</h2>
<p>The <code>co2_monitor</code> animation maps CO₂ ppm → color and adds slow motion so it doesnt feel like a stoplight:</p>
<ul>
<li><strong>&lt; 500 ppm</strong>: Cyan — outdoorlike fresh air.</li>
<li><strong>500799</strong>: Green — good.</li>
<li><strong>8001199</strong>: Yellow — getting stale.</li>
<li><strong>12001499</strong>: Orange — poor; youll feel it.</li>
<li><strong>15001999</strong>: Red — ventilate now.</li>
<li><strong>≥ 2000</strong>: Purple — the logo is politely yelling.</li>
</ul>
<p>I blend the readings with an <strong>exponential moving average</strong> and use <strong>hysteresis</strong> around thresholds so it doesnt pingpong colors when the room hovers at 800 ppm. Then I ease the current hue toward the target color and apply a very slow <strong>breathing brightness</strong>. The result feels alive but never flashy.</p>
<p>By default, the lights turn off from 20 to 6, then from 6 to 9, and 5 to 8 the standby red pulse is shown. From 9-5 on workdays the logo does it&rsquo;s daily job of Co2 monitoring. Well, technically it does that all the time, but during those hours it shows the Co2 level by it&rsquo;s color, the remaining time it just keeps a record of the Co2, temprature and humidity of room in the database.</p>
<hr>
<h2 id="6-the-code--a-guided-tour-no-giant-blob-promise">6) The Code — a guided tour (no giant blob, promise)</h2>
<p>This whole project runs from a single Python file. Think of it like a tiny orchestra:<br>
one thread conducts the <strong>LEDs</strong>, one listens politely to the <strong>CO₂ sensor</strong>, one keeps time as the <strong>scheduler</strong>, and a small <strong>web server</strong> hands you the baton when you want to improvise.</p>
<p>Below is what each section does, with just enough code to be useful (and not enough to make your eyes cross).</p>
<hr>
<h3 id="61-configuration-the-knobs">6.1 Configuration (the knobs)</h3>
<p>Let&rsquo;s define where the LEDs live, how bright they can get, and where to save tiny bits of state (schedule and sensor DB). All of it can be overridden with environment variables so you dont edit code to change pin numbers.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>LED_COUNT<span style="color:#f92672">=</span><span style="color:#ae81ff">78</span>, LED_PIN<span style="color:#f92672">=</span><span style="color:#ae81ff">18</span>, LED_BRIGHT<span style="color:#f92672">=</span><span style="color:#ae81ff">255</span>
</span></span><span style="display:flex;"><span>SCHEDULE_PATH<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;schedule.json&#34;</span>
</span></span><span style="display:flex;"><span>DB_PATH<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;sensor.db&#34;</span>
</span></span><span style="display:flex;"><span>SAFE_MODE<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span> <span style="color:#75715e"># hide flashy animations by default</span>
</span></span></code></pre></div><p><em>Why its like this:</em> simple, explicit defaults → less “why is it dark” debugging.</p>
<hr>
<h3 id="62-strip-setup--frame-diff-no-flicker-magic">6.2 Strip setup + frame-diff (no flicker magic)</h3>
<p>Now initialize <code>rpi_ws281x.PixelStrip</code> and put a <strong>shadow frame buffer</strong> in front of it.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>frame <span style="color:#f92672">=</span> [(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>)] <span style="color:#f92672">*</span> LED_COUNT
</span></span><span style="display:flex;"><span>_dirty <span style="color:#f92672">=</span> <span style="color:#66d9ef">False</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">_set_pixel</span>(i, r, g, b):
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># only touch hardware if the value actually changed</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> frame[i] <span style="color:#f92672">!=</span> (r,g,b):
</span></span><span style="display:flex;"><span> strip<span style="color:#f92672">.</span>setPixelColorRGB(i, r, g, b)
</span></span><span style="display:flex;"><span> frame[i] <span style="color:#f92672">=</span> (r,g,b)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">global</span> _dirty; _dirty <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">flush</span>():
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> _dirty: strip<span style="color:#f92672">.</span>show(); _dirty<span style="color:#f92672">=</span><span style="color:#66d9ef">False</span>
</span></span></code></pre></div><p><em>Why its like this:</em> LEDs are zen monks—disturb them only when you must. The frame-diff prevents those mysterious “one frame flash” moments that happen when you call <code>show()</code> with identical data.</p>
<hr>
<h3 id="63-a-tiny-color-wheel-helper">6.3 A tiny color wheel helper</h3>
<p>Classic rainbow helper used by a few animations:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">wheel</span>(pos):
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># 0..255 → (r,g,b)</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">...</span>
</span></span></code></pre></div><p><em>Why its like this:</em> I&rsquo;ll use it again and again; it keeps color math out of the animations.</p>
<hr>
<h3 id="64-state--orchestration">6.4 State &amp; orchestration</h3>
<p>Let&rsquo;s hold a registry of animations, a stop flag, and the currently playing thread.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>animations <span style="color:#f92672">=</span> {}
</span></span><span style="display:flex;"><span>stop_event <span style="color:#f92672">=</span> threading<span style="color:#f92672">.</span>Event()
</span></span><span style="display:flex;"><span>current_thread <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">register</span>(name, safe<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>):
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">wrap</span>(fn):
</span></span><span style="display:flex;"><span> animations[name] <span style="color:#f92672">=</span> {<span style="color:#e6db74">&#34;fn&#34;</span>: fn, <span style="color:#e6db74">&#34;safe&#34;</span>: safe}
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> fn
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> wrap
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">run_animation</span>(fn):
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># cooperative hand-off: stop current, start next</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> current_thread <span style="color:#f92672">and</span> current_thread<span style="color:#f92672">.</span>is_alive():
</span></span><span style="display:flex;"><span> stop_event<span style="color:#f92672">.</span>set(); current_thread<span style="color:#f92672">.</span>join()
</span></span><span style="display:flex;"><span> stop_event<span style="color:#f92672">.</span>clear()
</span></span><span style="display:flex;"><span> t <span style="color:#f92672">=</span> threading<span style="color:#f92672">.</span>Thread(target<span style="color:#f92672">=</span>fn, name<span style="color:#f92672">=</span>fn<span style="color:#f92672">.</span>__name__, daemon<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>)
</span></span><span style="display:flex;"><span> t<span style="color:#f92672">.</span>start(); globals()[<span style="color:#e6db74">&#34;current_thread&#34;</span>] <span style="color:#f92672">=</span> t
</span></span></code></pre></div><p><em>Why its like this:</em> adding a new animation is a one-liner <code>@register(&quot;name&quot;)</code>. Clean exits prevent torn frames and half-drawn comets.</p>
<hr>
<h3 id="65-animations-the-mood">6.5 Animations (the mood)</h3>
<p>Each animation is a loop that checks <code>stop_event.is_set()</code> and draws frames with <code>_set_pixel(...)</code> + <code>flush()</code>.</p>
<ul>
<li><strong><code>redpulse</code></strong> — calm breathing in red; great default.</li>
<li><strong><code>colorwave</code></strong> — slow rainbow that reads clearly behind diffusers.</li>
<li><strong><code>comet</code></strong> — a single head with a fading tail orbiting the logo.</li>
</ul>
<p>They all end with <code>finally: clear_strip()</code> so the panel doesnt freeze on the last pose if you stop mid-frame.</p>
<p><em>Why its like this:</em> cooperative loops + frame-diff = smooth, interruption-safe effects.</p>
<hr>
<h3 id="66-co-color-language-with-smoothing--hysteresis">6.6 CO₂ color language (with smoothing + hysteresis)</h3>
<p>The star of the show. Now map ppm to color <strong>bands</strong> and keep transitions human-pleasant.</p>
<ul>
<li><code>&lt; 500</code>: <strong>Cyan</strong> (fresh)</li>
<li><code>500799</code>: <strong>Green</strong></li>
<li><code>8001199</code>: <strong>Yellow</strong></li>
<li><code>12001499</code>: <strong>Orange</strong></li>
<li><code>15001999</code>: <strong>Red</strong></li>
<li><code>≥ 2000</code>: <strong>Purple</strong></li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>BANDS<span style="color:#f92672">=</span>[(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">500</span>,(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">214</span>,<span style="color:#ae81ff">255</span>)), (<span style="color:#ae81ff">500</span>,<span style="color:#ae81ff">800</span>,(<span style="color:#ae81ff">17</span>,<span style="color:#ae81ff">204</span>,<span style="color:#ae81ff">85</span>)), <span style="color:#f92672">...</span> (<span style="color:#ae81ff">2000</span>,<span style="color:#960050;background-color:#1e0010"></span>,(<span style="color:#ae81ff">123</span>,<span style="color:#ae81ff">44</span>,<span style="color:#ae81ff">191</span>))]
</span></span><span style="display:flex;"><span>EMA_ALPHA<span style="color:#f92672">=</span><span style="color:#ae81ff">0.15</span>; HYST<span style="color:#f92672">=</span><span style="color:#ae81ff">35</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Every tick:</span>
</span></span><span style="display:flex;"><span>ema <span style="color:#f92672">=</span> (<span style="color:#ae81ff">1</span><span style="color:#f92672">-</span>EMA_ALPHA)<span style="color:#f92672">*</span>ema <span style="color:#f92672">+</span> EMA_ALPHA<span style="color:#f92672">*</span>co2
</span></span><span style="display:flex;"><span>band <span style="color:#f92672">=</span> hysteresis_aware_band(ema, last_band, HYST)
</span></span><span style="display:flex;"><span>target <span style="color:#f92672">=</span> BANDS[band]<span style="color:#f92672">.</span>color
</span></span><span style="display:flex;"><span>current <span style="color:#f92672">=</span> lerp(current, target, <span style="color:#ae81ff">0.10</span>) <span style="color:#75715e"># gentle hue easing</span>
</span></span><span style="display:flex;"><span>level <span style="color:#f92672">=</span> breathe(<span style="color:#ae81ff">0.25</span> Hz, low<span style="color:#f92672">=</span><span style="color:#ae81ff">10</span>, high<span style="color:#f92672">=</span><span style="color:#ae81ff">220</span>)
</span></span><span style="display:flex;"><span>draw all pixels <span style="color:#f92672">=</span> current <span style="color:#f92672">*</span> level
</span></span></code></pre></div><p><em>Why its like this:</em> EMA + hysteresis prevents “800↔801 disco.” The slow breathing keeps the panel feeling alive, not like a traffic light.</p>
<hr>
<h3 id="67-optional-scd41-sensor-thread-the-polite-listener">6.7 Optional SCD41 sensor thread (the polite listener)</h3>
<p>If the SCD41 is present, a <strong>dedicated thread</strong> owns I²C and updates a global <code>co2_data</code> dict every few seconds. It also logs to a tiny SQLite database.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">_start_sensor</span>():
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">try</span>:
</span></span><span style="display:flex;"><span> scd4x <span style="color:#f92672">=</span> adafruit_scd4x<span style="color:#f92672">.</span>SCD4X(i2c); scd4x<span style="color:#f92672">.</span>start_periodic_measurement()
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">except</span>: <span style="color:#66d9ef">return</span> <span style="color:#75715e"># sensor optional</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">loop</span>():
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">while</span> <span style="color:#66d9ef">True</span>:
</span></span><span style="display:flex;"><span> time<span style="color:#f92672">.</span>sleep(<span style="color:#ae81ff">5</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> scd4x<span style="color:#f92672">.</span>data_ready:
</span></span><span style="display:flex;"><span> co2_data<span style="color:#f92672">.</span>update({<span style="color:#f92672">...</span>})
</span></span><span style="display:flex;"><span> db<span style="color:#f92672">.</span>insert(timestamp, co2, temperature, humidity)
</span></span><span style="display:flex;"><span> threading<span style="color:#f92672">.</span>Thread(target<span style="color:#f92672">=</span>loop, daemon<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>)<span style="color:#f92672">.</span>start()
</span></span></code></pre></div><p><em>Why its like this:</em> one thread owns the bus → no read contention. The controller keeps working even without a sensor.</p>
<hr>
<h3 id="68-scheduler--90-minute-override-humans-win-then-reset">6.8 Scheduler + 90-minute override (humans win, then reset)</h3>
<p>A background thread asks, every few seconds, <strong>which</strong> mode should be running:</p>
<ol>
<li>If the user chose something recently (override), respect it for <code>TTL = 90 min</code> (or the duration set in the UI).</li>
<li>Otherwise, apply the <strong>default schedule</strong> (Wednesday 1417 → <code>slow</code>, else <code>redpulse</code>).</li>
<li>Save/load the schedule atomically to <code>schedule.json</code>.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">scheduler_pick</span>():
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> now <span style="color:#f92672">&lt;</span> override_until: <span style="color:#66d9ef">return</span> override_mode
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> row <span style="color:#f92672">in</span> load_schedule():
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> matches(now, row): <span style="color:#66d9ef">return</span> row[<span style="color:#e6db74">&#34;mode&#34;</span>]
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;redpulse&#34;</span>
</span></span></code></pre></div><p><em>Why its like this:</em> you can play DJ for a meeting, and the room quietly returns to its routine afterward.</p>
<hr>
<h3 id="69-the-web-panel-tiny-flask-big-buttons">6.9 The web panel (tiny Flask, big buttons)</h3>
<p>Three pages, no fuss:</p>
<ul>
<li><code>/status</code> — live JSON (<code>/status_data</code>) every second → current mode + CO₂/Temp/Humidity.</li>
<li><code>/control</code> — grid of same-size buttons (respects <strong>Safe Mode</strong>), optional <strong>duration</strong> (0180 min) with validation.</li>
<li><code>/schedule</code> — simple table editor; <strong>Add Row</strong> and <strong>Save</strong>; writes atomically.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#a6e22e">@app.post</span>(<span style="color:#e6db74">&#34;/set&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">set_mode</span>():
</span></span><span style="display:flex;"><span> mode <span style="color:#f92672">=</span> request<span style="color:#f92672">.</span>form[<span style="color:#e6db74">&#34;mode&#34;</span>]
</span></span><span style="display:flex;"><span> minutes <span style="color:#f92672">=</span> clamp( int(form[<span style="color:#e6db74">&#34;duration&#34;</span>]), <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">180</span> )
</span></span><span style="display:flex;"><span> set_override(mode, minutes)
</span></span><span style="display:flex;"><span> run_animation(animations[mode][<span style="color:#e6db74">&#34;fn&#34;</span>])
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> redirect(<span style="color:#e6db74">&#34;/control&#34;</span>)
</span></span></code></pre></div><p><em>Why its like this:</em> minimal routes are easier to maintain and dont compete with the art piece.</p>
<hr>
<h3 id="610-boot-choreography">6.10 Boot choreography</h3>
<p>At startup I:</p>
<ol>
<li>Try the <strong>sensor thread</strong> (if hardware is there).</li>
<li>Start the <strong>scheduler thread</strong>.</li>
<li>Immediately play <strong>redpulse</strong> (so theres a friendly glow right away).</li>
<li>Start Flask; on shutdown I <strong>clear the strip</strong>.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> __name__ <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;__main__&#34;</span>:
</span></span><span style="display:flex;"><span> _start_sensor()
</span></span><span style="display:flex;"><span> threading<span style="color:#f92672">.</span>Thread(target<span style="color:#f92672">=</span>scheduler_loop, daemon<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>)<span style="color:#f92672">.</span>start()
</span></span><span style="display:flex;"><span> run_animation(redpulse)
</span></span><span style="display:flex;"><span> app<span style="color:#f92672">.</span>run(host<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;0.0.0.0&#34;</span>, port<span style="color:#f92672">=</span><span style="color:#ae81ff">5000</span>)
</span></span></code></pre></div><p><em>Why its like this:</em> the room sees something alive instantly; the scheduler will swap in the “real” choice within a few seconds.</p>
<hr>
<h3 id="tldr-but-keep-the-vibes">TL;DR (but keep the vibes)</h3>
<ul>
<li><strong>Frame-diff</strong> = no flashes.</li>
<li><strong>EMA + hysteresis</strong> = smooth, truthful color.</li>
<li><strong>Breathing</strong> = presence, not signage.</li>
<li><strong>Threads per thing</strong> (LEDs / sensor / scheduler) = no fights.</li>
<li><strong>Atomic files</strong> = no corrupted schedules.</li>
<li><strong>Safe Mode</strong> by default = people &gt; pixels.</li>
</ul>
<p>Thats it — the code behaves like a considerate colleague: dependable, quiet, and occasionally very pretty.</p>
<h3 id="why-this-shape-of-code">Why this shape of code?</h3>
<ul>
<li><strong>One thread per hardware thing.</strong> The sensor thread owns I²C. The animation thread owns LEDs. The scheduler just decides <em>what</em> to run and when. No bus fights.</li>
<li><strong>Frame diff.</strong> I only call <code>strip.show()</code> when a pixel actually changes. Thats why it doesnt randomly flash during web requests.</li>
<li><strong>Atomic schedule saves.</strong> The code writes to a temporary file and replaces the old one so a midsave power cut cant corrupt the schedule.</li>
</ul>
<blockquote>
<p>No, you dont <em>need</em> the sensor. If its not connected, the app still runs; <code>co2_monitor</code> just sits at the default value. Well, technically it shows NaN as the numbers! But it is fun to have a sensor, don&rsquo;t you agree?</p></blockquote>
<hr>
<h2 id="7-sensor-database--what-it-stores-where-it-lives-and-how-far-it-goes">7) Sensor Database — what it stores, where it lives, and how far it goes</h2>
<p>This project logs room conditions to a <strong>tiny SQLite database</strong> so you can graph trends, spot stuffy meetings, and keep a record without running a separate server.</p>
<h3 id="whats-stored">Whats stored</h3>
<p>A single table called <code>readings</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">CREATE</span> <span style="color:#66d9ef">TABLE</span> <span style="color:#66d9ef">IF</span> <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">EXISTS</span> readings (
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">timestamp</span> TEXT <span style="color:#66d9ef">PRIMARY</span> <span style="color:#66d9ef">KEY</span>, <span style="color:#75715e">-- &#34;YYYY-MM-DD HH:MM:SS&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> co2 INTEGER, <span style="color:#75715e">-- ppm
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> temperature REAL, <span style="color:#75715e">-- °C
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> humidity REAL <span style="color:#75715e">-- %
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>);
</span></span></code></pre></div><ul>
<li>One row per sample (typically every <strong>560 seconds</strong>; slow it down to reduce writes).</li>
<li><code>timestamp</code> is the primary key → easy “latest” queries and natural time ordering.</li>
</ul>
<h3 id="where-the-file-lives">Where the file lives</h3>
<ul>
<li>Path is configurable via env var <code>DB_PATH</code> (see your systemd unit).</li>
<li>Default: <code>sensor.db</code> in the apps working directory (e.g. <code>/home/pi/inet-led/sensor.db</code>).</li>
</ul>
<p>Check size:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ls -lh /home/pi/inet-led/sensor.db
</span></span></code></pre></div><h3 id="how-writes-happen-and-why-its-safe">How writes happen (and why its safe)</h3>
<ul>
<li>The <strong>sensor thread</strong> owns I²C and inserts a row only when the sensor reports <code>data_ready</code>.</li>
<li>Inserts are short and journaling protects against power loss.</li>
<li>For friendlier read/write concurrency, enable WAL once at startup:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span>PRAGMA journal_mode <span style="color:#f92672">=</span> WAL;
</span></span><span style="display:flex;"><span>PRAGMA synchronous <span style="color:#f92672">=</span> NORMAL;
</span></span></code></pre></div><h3 id="typical-size--retention">Typical size &amp; retention</h3>
<p>Back-of-envelope:</p>
<ul>
<li>~100200 bytes per row (including overhead).</li>
<li>1 sample/minute → ~1,440 rows/day → <strong>~150300 KB/day</strong><strong>55110 MB/year</strong>.</li>
<li>If you log every 5 seconds, multiply by ~12 (consider slower logging or downsampling).</li>
</ul>
<p>Prune old data (keep last 90 days):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">DELETE</span> <span style="color:#66d9ef">FROM</span> readings
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span> <span style="color:#66d9ef">timestamp</span> <span style="color:#f92672">&lt;</span> date(<span style="color:#e6db74">&#39;now&#39;</span>,<span style="color:#e6db74">&#39;-90 day&#39;</span>);
</span></span></code></pre></div><p>(Optionally run <code>VACUUM;</code> after large deletes to reclaim file space.)</p>
<h3 id="useful-queries">Useful queries</h3>
<p><strong>Latest reading:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">FROM</span> readings <span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> <span style="color:#66d9ef">timestamp</span> <span style="color:#66d9ef">DESC</span> <span style="color:#66d9ef">LIMIT</span> <span style="color:#ae81ff">1</span>;
</span></span></code></pre></div><p><strong>Range for charts (last 7 days):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">FROM</span> readings
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span> <span style="color:#66d9ef">timestamp</span> <span style="color:#f92672">&gt;=</span> datetime(<span style="color:#e6db74">&#39;now&#39;</span>,<span style="color:#e6db74">&#39;-7 day&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> <span style="color:#66d9ef">timestamp</span>;
</span></span></code></pre></div><p><strong>Downsample to hourly averages (30 days):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> strftime(<span style="color:#e6db74">&#39;%Y-%m-%d %H:00:00&#39;</span>, <span style="color:#66d9ef">timestamp</span>) <span style="color:#66d9ef">AS</span> hour,
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">AVG</span>(co2) <span style="color:#66d9ef">AS</span> co2, <span style="color:#66d9ef">AVG</span>(temperature) <span style="color:#66d9ef">AS</span> t, <span style="color:#66d9ef">AVG</span>(humidity) <span style="color:#66d9ef">AS</span> h
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> readings
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span> <span style="color:#66d9ef">timestamp</span> <span style="color:#f92672">&gt;=</span> datetime(<span style="color:#e6db74">&#39;now&#39;</span>,<span style="color:#e6db74">&#39;-30 day&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">GROUP</span> <span style="color:#66d9ef">BY</span> hour
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> hour;
</span></span></code></pre></div><p><strong>Export to CSV (example via sqlite3 CLI):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sqlite3 /home/pi/inet-led/sensor.db <span style="color:#e6db74">&lt;&lt;&#39;SQL&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">.headers on
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">.mode csv
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">.output /home/pi/inet-led/export_readings.csv
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">SELECT * FROM readings ORDER BY timestamp;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">.output stdout
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">SQL</span>
</span></span></code></pre></div><h3 id="sd-card-friendliness">SD card friendliness</h3>
<ul>
<li>Choose a sensible interval (e.g., <strong>3060 s</strong>).</li>
<li>Optionally buffer in memory and write every N samples.</li>
<li>Prefer WAL mode; periodically prune &amp; vacuum.</li>
<li>If you care about card wear, place the DB on USB/SSD.</li>
</ul>
<h3 id="backups--restore">Backups &amp; restore</h3>
<p><strong>Nightly backup (simple):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sqlite3 /home/pi/inet-led/sensor.db <span style="color:#e6db74">&#34;.backup &#39;/home/pi/backup/sensor-</span><span style="color:#66d9ef">$(</span>date +%F<span style="color:#66d9ef">)</span><span style="color:#e6db74">.db&#39;&#34;</span>
</span></span></code></pre></div><p><strong>Restore:</strong></p>
<ol>
<li><code>sudo systemctl stop inet-led</code></li>
<li>Copy backup file back to <code>/home/pi/inet-led/sensor.db</code></li>
<li><code>sudo systemctl start inet-led</code></li>
</ol>
<h3 id="security--permissions">Security &amp; permissions</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>chown pi:pi /home/pi/inet-led/sensor.db
</span></span><span style="display:flex;"><span>chmod <span style="color:#ae81ff">600</span> /home/pi/inet-led/sensor.db
</span></span></code></pre></div><p>Keep the app directory non-world-readable.</p>
<h3 id="is-sqlite-the-right-choice">Is SQLite the right choice?</h3>
<p><strong>Yes, for a single Pi</strong> logging every few seconds and rendering local charts:</p>
<ul>
<li>✅ Zero admin, a single file, reliable journaling, great performance at this scale.</li>
<li>⚠️ One writer at a time (I only have the sensor thread writing, so its fine).</li>
<li>⬆️ If you later need multi-device ingestion, alerts, or &gt;10s of millions of rows, consider a time-series DB (TimescaleDB/InfluxDB/VictoriaMetrics) and migrate using CSV exports.</li>
</ul>
<h3 id="tldr">TL;DR</h3>
<p>SQLite is perfect here: <strong>simple, robust, easy to back up</strong>. Start with it, and only upgrade when your ambitions outgrow a single Raspberry Pi.</p>
<hr>
<h2 id="8-run-at-boot-systemd">8) Run at Boot (systemd)</h2>
<p>I wrote this simple systemd to <code>/etc/systemd/system/inet-led.service</code> so that it runs the script when RPi boots up:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#66d9ef">[Unit]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Description</span><span style="color:#f92672">=</span><span style="color:#e6db74">INET LED Panel</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">After</span><span style="color:#f92672">=</span><span style="color:#e6db74">network.target</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">[Service]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ExecStart</span><span style="color:#f92672">=</span><span style="color:#e6db74">/usr/bin/python3 /home/pi/inet-led/inet_led_panel.py</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">WorkingDirectory</span><span style="color:#f92672">=</span><span style="color:#e6db74">/home/pi/inet-led</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Restart</span><span style="color:#f92672">=</span><span style="color:#e6db74">always</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">User</span><span style="color:#f92672">=</span><span style="color:#e6db74">pi</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Environment</span><span style="color:#f92672">=</span><span style="color:#e6db74">LED_COUNT=78</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Environment</span><span style="color:#f92672">=</span><span style="color:#e6db74">SCHEDULE_FILE=/home/pi/inet-led/schedule.json</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Environment</span><span style="color:#f92672">=</span><span style="color:#e6db74">DB_PATH=/home/pi/inet-led/sensor.db</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">[Install]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">WantedBy</span><span style="color:#f92672">=</span><span style="color:#e6db74">multi-user.target</span>
</span></span></code></pre></div><p>Then you can run:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo systemctl daemon-reload
</span></span><span style="display:flex;"><span>sudo systemctl enable --now inet-led
</span></span><span style="display:flex;"><span>journalctl -u inet-led -f
</span></span></code></pre></div><p>to control the systemd service.</p>
<hr>
<h2 id="9-soldering-notes-a-love-letter-to-hot-glue">9) Soldering Notes (a love letter to hot glue)</h2>
<ul>
<li><strong>Tin first, solder second.</strong> Tiny pads like tiny puddles. Fast in/out, no lifted pads.</li>
<li><strong>Stagger joints</strong> so nothing stacks under the diffuser.</li>
<li><strong>Heatshrink + a dot of hot glue</strong> = happier future you.</li>
<li><strong>Continuity before power.</strong> Multimeter first, electrons last.</li>
<li>If a pad <em>does</em> lift: magnet wire to a trace, UV mask to seal. Ugly heroics, but it works.</li>
</ul>
<p>Besides the soldering part, I also used m2 screws to fix the diffusers. Initially magnets were suggested, but I felt like figuring that out might take me long, and screws just feel more maintainable&hellip; So&hellip; yea.</p>
<hr>
<h2 id="10-why-these-design-choices">10) Why these design choices?</h2>
<ul>
<li><strong>Calm motion, not flashy.</strong> Meeting rooms breed fatigue; the light should be a helper, not a distraction.</li>
<li><strong>Cyan→Purple language.</strong> Easy to learn, visible at a glance, meaningful without numbers.</li>
<li><strong>White charts, dark UI.</strong> Data should be legible on a projector; buttons shouldnt shout.</li>
<li><strong>Safe Mode default.</strong> People first. Demos are optin.</li>
<li><strong>PLA body, PLA diffuser.</strong> Fast and easy rapid prototyping, easy and fast printing.</li>
</ul>
<hr>
<h2 id="11-what-i-learned">11) What I learned</h2>
<ul>
<li>A logo is a better messenger than a dashboard.</li>
<li>Everyone knows what <strong>orange</strong> means without a legend.</li>
<li>If you show the room a mirror, it corrects itself. Someone will open the door long before you have to ask, and if someone notices the orange/red color of the logo, they would hint the end of the gathering!</li>
<li>Sometimes debugging is not fun at all, and you want to bang your head to the wall, but try not to! Life is short. ^^</li>
</ul>
<hr>
<h2 id="12-final-notes">12) Final notes</h2>
<p>I did this project as a fun distraction and &ldquo;not work&rdquo; as my advisor tells me to do all the time. My advisor provided the LED strip, RPi zero 2 W, and the voltage divider. I got the sensor, designed and coded everything else, and had a lot of fun doing so. I&rsquo;m so so thankful of my advisor for this cool idea, and all the support. It&rsquo;s not the first time I&rsquo;ve been gifted such toys, and as I have recieved other things after that, I know it&rsquo;s not the last.</p>
<p>There is a bug I never was able to figure out, sometimes when I stop the script, I get segmentation fault. I know it is not directly my code with extensive testing, and that the problem is with the library, but I never understood why it happens. Let&rsquo;s hope that doesn&rsquo;t turn into a backdoor into my logo. * Shakingly crosses fingers and sighs in distress!*</p>
<p>The whole project took around 8 days, 4 of which were part of a long weekend. I did do some &ldquo;not work&rdquo; as Tobias always tells me to do, and honestly it was fun and refreshing! I really enjoyed it. I don&rsquo;t know how the group feels/thinks about the logo, but I love it! I hope it won&rsquo;t die after I leave, but even if so, so be it. I had my fun with it. :)</p>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/flask/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/flask/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/flask/"></head></html>

View file

@ -0,0 +1,10 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Geyser | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/geyser/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/geyser/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/geyser/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/geyser/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Geyser"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Geyser"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/>Tags</a></div><h1>Geyser
<a href=/tags/geyser/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><header class=entry-header><h2 class=entry-hint-parent>Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases</h2></header><div class=entry-content><p>How I containerized a Java Minecraft server with Geyser, hit a /var space wall, and locked the server to the latest stable release.</p></div><footer class=entry-footer><span title='2025-09-29 09:06:29 +0000 UTC'>September 29, 2025</span>&nbsp;·&nbsp;3 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,259 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Geyser on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/geyser/</link><description>Recent content in Geyser on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Mon, 29 Sep 2025 09:06:29 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/geyser/index.xml" rel="self" type="application/rss+xml"/><item><title>Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/</link><pubDate>Mon, 29 Sep 2025 09:06:29 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/</guid><description>How I containerized a Java Minecraft server with Geyser, hit a /var space wall, and locked the server to the latest stable release.</description><content:encoded><![CDATA[<h2 id="why">Why</h2>
<p>Ive been running a Java Minecraft world (with Bedrock players via <strong>Geyser</strong>) in <code>tmux</code>. I moved it to Docker for easier updates, backups, and restarts. Along the way I hit:</p>
<ul>
<li><code>failed to register layer: ... no space left on device</code></li>
<li>Client saying: <strong>“Outdated client, please use 1.21.9 Pre-Release 2”</strong></li>
<li>Wanting config in a neat <code>.env</code> and <strong>no whitelist</strong></li>
</ul>
<p>Heres exactly what I did.</p>
<hr>
<h2 id="folder-layout">Folder layout</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>~/minecraft/
</span></span><span style="display:flex;"><span>├─ docker-compose.yml
</span></span><span style="display:flex;"><span>├─ .env
</span></span><span style="display:flex;"><span>└─ plugins/
</span></span></code></pre></div><hr>
<h2 id="env-secrets--knobs"><code>.env</code> (secrets &amp; knobs)</h2>
<p>Use the <strong>latest stable</strong> (not snapshots/pre-releases) by setting <code>VERSION=LATEST</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-env" data-lang="env"><span style="display:flex;"><span><span style="color:#75715e"># Minecraft server configuration</span>
</span></span><span style="display:flex;"><span>EULA<span style="color:#f92672">=</span>TRUE
</span></span><span style="display:flex;"><span>TYPE<span style="color:#f92672">=</span>PAPER
</span></span><span style="display:flex;"><span>VERSION<span style="color:#f92672">=</span>LATEST
</span></span><span style="display:flex;"><span>MEMORY<span style="color:#f92672">=</span>4G
</span></span><span style="display:flex;"><span>USE_AIKAR_FLAGS<span style="color:#f92672">=</span>true
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># RCON (remote console)</span>
</span></span><span style="display:flex;"><span>ENABLE_RCON<span style="color:#f92672">=</span>true
</span></span><span style="display:flex;"><span>RCON_PASSWORD<span style="color:#f92672">=</span>superSecretPassword123
</span></span></code></pre></div><blockquote>
<p>I dont use a whitelist, so no <code>WHITELIST</code>/<code>ENFORCE_WHITELIST</code> variables.</p></blockquote>
<hr>
<h2 id="docker-composeyml"><code>docker-compose.yml</code></h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">mc</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">itzg/minecraft-server:latest</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">mc</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;25565:25565&#34;</span> <span style="color:#75715e"># Java</span>
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;19132:19132/udp&#34;</span> <span style="color:#75715e"># Bedrock via Geyser plugin</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">env_file</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">.env</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">mc-data:/data</span>
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">./plugins:/plugins:ro</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">mc-data</span>: {}
</span></span></code></pre></div><blockquote>
<p>The <code>version:</code> key in Compose is obsolete now, so I dropped it.</p></blockquote>
<hr>
<h2 id="add-geyser-and-optional-floodgate-as-plugins">Add Geyser (and optional Floodgate) as plugins</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir -p ~/minecraft/plugins
</span></span><span style="display:flex;"><span>cd ~/minecraft/plugins
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Geyser (Spigot/Paper)</span>
</span></span><span style="display:flex;"><span>wget https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot -O Geyser-Spigot.jar
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Floodgate (optional: Bedrock accounts without Java linking)</span>
</span></span><span style="display:flex;"><span>wget https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot -O Floodgate-Spigot.jar
</span></span></code></pre></div><p>Start it up:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose up -d
</span></span></code></pre></div><p>On first run, Geyser writes its config to <code>/data/plugins/Geyser-Spigot/</code>. Open UDP <strong>19132</strong> on your firewall.</p>
<hr>
<h2 id="hit-a-wall-var-ran-out-of-space">Hit a wall: <code>/var</code> ran out of space</h2>
<p>Docker stores layers at <code>/var/lib/docker</code> by default. My <code>/var</code> LV was tiny:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 448 25"
>
<g transform='translate(8,16)'>
<path d='M 404,8 L 404,24' fill='none' stroke='currentColor'></path>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>%</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>r</text>
</g>
</svg>
</div>
<h3 id="quick-cleanup">Quick cleanup</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker system df
</span></span><span style="display:flex;"><span>docker system prune -f
</span></span><span style="display:flex;"><span>docker builder prune -af
</span></span><span style="display:flex;"><span>sudo apt-get clean
</span></span><span style="display:flex;"><span>sudo journalctl --vacuum-size<span style="color:#f92672">=</span>100M
</span></span></code></pre></div><p>If thats not enough, you have two good options:</p>
<h3 id="option-a--move-dockers-data-off-var">Option A — Move Dockers data off <code>/var</code></h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo systemctl stop docker
</span></span><span style="display:flex;"><span>sudo mkdir -p /home/docker
</span></span><span style="display:flex;"><span>sudo rsync -aHAX --info<span style="color:#f92672">=</span>progress2 /var/lib/docker/ /home/docker/
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#39;{ &#34;data-root&#34;: &#34;/home/docker&#34; }&#39;</span> | sudo tee /etc/docker/daemon.json
</span></span><span style="display:flex;"><span>sudo systemctl start docker
</span></span><span style="display:flex;"><span>docker info | grep <span style="color:#e6db74">&#34;Docker Root Dir&#34;</span>
</span></span></code></pre></div><p>If all looks good, free the old space:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo rm -rf /var/lib/docker/*
</span></span></code></pre></div><h3 id="option-b--grow-var-lvm">Option B — Grow <code>/var</code> (LVM)</h3>
<p>Check free space in the VG:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo vgdisplay <span style="color:#75715e"># look for &#34;Free PE / Size&#34;</span>
</span></span></code></pre></div><p>If available, extend <code>/var</code> by +5G:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo lvextend -L +5G /dev/mapper/ubuntu--vg-var
</span></span><span style="display:flex;"><span>sudo resize2fs /dev/mapper/ubuntu--vg-var
</span></span></code></pre></div><hr>
<h2 id="the-version-mismatch-pre-release-vs-stable">The version mismatch: pre-release vs stable</h2>
<p>My logs showed:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 440 25"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>2</text>
</g>
</svg>
</div>
<p>That happens if the server pulls a pre-release build (or if <code>VERSION=LATEST</code>). Fix:</p>
<ol>
<li>Ensure <code>.env</code> has:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-env" data-lang="env"><span style="display:flex;"><span>VERSION<span style="color:#f92672">=</span>LATEST_RELEASE
</span></span></code></pre></div></li>
<li>Recreate:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose down
</span></span><span style="display:flex;"><span>docker compose pull
</span></span><span style="display:flex;"><span>docker compose up -d
</span></span></code></pre></div></li>
<li>Verify:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker logs mc | grep <span style="color:#e6db74">&#34;Starting minecraft server version&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># -&gt; Starting minecraft server version 1.21.1 (example)</span>
</span></span></code></pre></div></li>
</ol>
<blockquote>
<p>Tip: If you <strong>want to pin</strong> and avoid auto-updates entirely, set <code>VERSION=1.21.1</code> (or whatever stable youve validated).</p></blockquote>
<hr>
<h2 id="no-whitelist">No whitelist</h2>
<p>Because I dont set <code>WHITELIST</code>/<code>ENFORCE_WHITELIST</code>, anyone can join (subject to online-mode, bans, and Geyser/Floodgate auth settings). Manage ops/permissions via:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker exec -it mc rcon-cli <span style="color:#e6db74">&#34;op YourJavaIGN&#34;</span>
</span></span></code></pre></div><p>(or edit <code>/data/ops.json</code>).</p>
<hr>
<h2 id="backup--updates">Backup &amp; updates</h2>
<ul>
<li>World/data live under the <code>mc-data</code> volume → back up <code>/data</code> regularly.</li>
<li>Update cleanly:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose pull <span style="color:#f92672">&amp;&amp;</span> docker compose up -d
</span></span></code></pre></div></li>
<li>Watch space:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>watch -n5 <span style="color:#e6db74">&#39;df -h /var; docker system df&#39;</span>
</span></span></code></pre></div></li>
</ul>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Put <strong>Geyser/Floodgate</strong> jars in <code>./plugins</code> and expose <strong>19132/udp</strong>.</li>
<li>Keep configs in <code>.env</code>.</li>
<li>Fix <code>/var</code> space by pruning, <strong>moving Dockers data-root</strong>, or <strong>extending <code>/var</code></strong> via LVM.</li>
<li>Verify version in logs after each change.</li>
</ul>
<p>Happy block-breaking! 🧱🚀</p>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/geyser/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/geyser/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/geyser/"></head></html>

View file

@ -0,0 +1,12 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Hedgedoc | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hedgedoc/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hedgedoc/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hedgedoc/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hedgedoc/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Hedgedoc"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Hedgedoc"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/>Tags</a></div><h1>Hedgedoc
<a href=/tags/hedgedoc/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><figure class=entry-cover><img loading=lazy src=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/images/hedgedoc-cover.png alt="HedgeDoc collaborative editing"></figure><header class=entry-header><h2 class=entry-hint-parent>Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt</h2></header><div class=entry-content><p>HedgeDoc is an open-source collaborative Markdown editor. Think Google Docs for Markdown: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post well walk through setting up your own instance on a server, secured with HTTPS.
Prerequisites A Linux server with Docker and Docker Compose A domain name pointing to your server (e.g. notes.alipourimjourneys.ir) Nginx installed for reverse proxying Certbot for Lets Encrypt certificates 1. Create the project directory mkdir ~/hedgedoc && cd ~/hedgedoc 2. Create .env POSTGRES_PASSWORD=ChangeThisStrongPassword HD_DOMAIN=notes.alipourimjourneys.ir Generate a strong password with:
...</p></div><footer class=entry-footer><span title='2025-08-29 00:00:00 +0000 UTC'>August 29, 2025</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

View file

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Hedgedoc on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hedgedoc/</link><description>Recent content in Hedgedoc on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Fri, 29 Aug 2025 00:00:00 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hedgedoc/index.xml" rel="self" type="application/rss+xml"/><item><title>Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/</link><pubDate>Fri, 29 Aug 2025 00:00:00 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/</guid><description>&lt;p>HedgeDoc is an open-source collaborative Markdown editor. Think &lt;em>Google Docs for Markdown&lt;/em>: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post well walk through setting up your own instance on a server, secured with HTTPS.&lt;/p>
&lt;hr>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;ul>
&lt;li>A Linux server with &lt;strong>Docker&lt;/strong> and &lt;strong>Docker Compose&lt;/strong>&lt;/li>
&lt;li>A domain name pointing to your server (e.g. &lt;code>notes.alipourimjourneys.ir&lt;/code>)&lt;/li>
&lt;li>&lt;strong>Nginx&lt;/strong> installed for reverse proxying&lt;/li>
&lt;li>&lt;strong>Certbot&lt;/strong> for Lets Encrypt certificates&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="1-create-the-project-directory">1. Create the project directory&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>mkdir ~/hedgedoc &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> cd ~/hedgedoc&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;hr>
&lt;h2 id="2-create-env">2. Create &lt;code>.env&lt;/code>&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-env" data-lang="env">&lt;span style="display:flex;">&lt;span>POSTGRES_PASSWORD&lt;span style="color:#f92672">=&lt;/span>ChangeThisStrongPassword
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>HD_DOMAIN&lt;span style="color:#f92672">=&lt;/span>notes.alipourimjourneys.ir&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Generate a strong password with:&lt;/p></description><content:encoded><![CDATA[<p>HedgeDoc is an open-source collaborative Markdown editor. Think <em>Google Docs for Markdown</em>: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post well walk through setting up your own instance on a server, secured with HTTPS.</p>
<hr>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>A Linux server with <strong>Docker</strong> and <strong>Docker Compose</strong></li>
<li>A domain name pointing to your server (e.g. <code>notes.alipourimjourneys.ir</code>)</li>
<li><strong>Nginx</strong> installed for reverse proxying</li>
<li><strong>Certbot</strong> for Lets Encrypt certificates</li>
</ul>
<hr>
<h2 id="1-create-the-project-directory">1. Create the project directory</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir ~/hedgedoc <span style="color:#f92672">&amp;&amp;</span> cd ~/hedgedoc</span></span></code></pre></div>
<hr>
<h2 id="2-create-env">2. Create <code>.env</code></h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-env" data-lang="env"><span style="display:flex;"><span>POSTGRES_PASSWORD<span style="color:#f92672">=</span>ChangeThisStrongPassword
</span></span><span style="display:flex;"><span>HD_DOMAIN<span style="color:#f92672">=</span>notes.alipourimjourneys.ir</span></span></code></pre></div>
<p>Generate a strong password with:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>openssl rand -base64 <span style="color:#ae81ff">32</span></span></span></code></pre></div>
<hr>
<h2 id="3-create-docker-composeyml">3. Create <code>docker-compose.yml</code></h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">version</span>: <span style="color:#e6db74">&#34;3.9&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">db</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">postgres:16</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_USER</span>: <span style="color:#ae81ff">hedgedoc</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_PASSWORD</span>: <span style="color:#ae81ff">${POSTGRES_PASSWORD}</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_DB</span>: <span style="color:#ae81ff">hedgedoc</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">db:/var/lib/postgresql/data</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">hedgedoc</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">quay.io/hedgedoc/hedgedoc:1.10.2</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">depends_on</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">db</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_DB_URL</span>: <span style="color:#ae81ff">postgres://hedgedoc:${POSTGRES_PASSWORD}@db:5432/hedgedoc</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_DOMAIN</span>: <span style="color:#ae81ff">${HD_DOMAIN}</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_PROTOCOL_USESSL</span>: <span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_URL_ADDPORT</span>: <span style="color:#e6db74">&#34;false&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_PORT</span>: <span style="color:#e6db74">&#34;3000&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_EMAIL</span>: <span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_ALLOW_EMAIL_REGISTER</span>: <span style="color:#e6db74">&#34;false&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">uploads:/hedgedoc/public/uploads</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;127.0.0.1:3000:3000&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">db</span>:
</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">uploads:</span></span></span></code></pre></div>
<p>Bring it up:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose up -d</span></span></code></pre></div>
<hr>
<h2 id="4-get-a-lets-encrypt-certificate">4. Get a Lets Encrypt certificate</h2>
<p>Request a cert with a <strong>DNS challenge</strong>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo certbot certonly --manual --preferred-challenges dns -d notes.alipourimjourneys.ir -m you@example.com --agree-tos --no-eff-email</span></span></code></pre></div>
<p>Add the TXT record certbot asks for, wait for DNS to propagate, then continue.<br>
Certificates will be in:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>/etc/letsencrypt/live/notes.alipourimjourneys.ir/</span></span></code></pre></div>
<hr>
<h2 id="5-configure-nginx">5. Configure Nginx</h2>
<p>Create <code>/etc/nginx/sites-available/notes.alipourimjourneys.ir</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">notes.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">80</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:80</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">return</span> <span style="color:#ae81ff">301</span> <span style="color:#e6db74">https://</span>$host$request_uri;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">notes.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/notes.alipourimjourneys.ir/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/notes.alipourimjourneys.ir/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:3000</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Real-IP</span> $remote_addr;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $proxy_add_x_forwarded_for;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> <span style="color:#e6db74">https</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_http_version</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">.1</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">&#34;upgrade&#34;</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p>Enable the config and reload Nginx:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo ln -s /etc/nginx/sites-available/notes.alipourimjourneys.ir /etc/nginx/sites-enabled/
</span></span><span style="display:flex;"><span>sudo nginx -t <span style="color:#f92672">&amp;&amp;</span> sudo systemctl reload nginx</span></span></code></pre></div>
<hr>
<h2 id="6-create-users">6. Create users</h2>
<p>Because we disabled self-registration, create accounts manually:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose exec hedgedoc ./bin/manage_users --add alice@example.com</span></span></code></pre></div>
<p>Youll be prompted for a password.<br>
To reset a password later:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose exec hedgedoc ./bin/manage_users --reset alice@example.com</span></span></code></pre></div>
<hr>
<h2 id="7-done">7. Done!</h2>
<p>Visit <a href="https://notes.alipourimjourneys.ir">https://notes.alipourimjourneys.ir</a> and log in with the user you created. You now have your own collaborative Markdown editor 🎉</p>
<hr>
<p><strong>Extras:</strong></p>
<ul>
<li>Backups: dump the PostgreSQL database and save the <code>uploads</code> volume.</li>
<li>Upgrades: <code>docker pull quay.io/hedgedoc/hedgedoc:latest &amp;&amp; docker compose up -d</code>.</li>
<li>Integrations: HedgeDoc supports S3/MinIO image storage, GitHub/GitLab/Google login, and more.</li>
</ul>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hedgedoc/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hedgedoc/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hedgedoc/"></head></html>

View file

@ -0,0 +1,14 @@
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Hugo | AlipourIm journeys</title>
<meta name=keywords content><meta name=description content><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hugo/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate type=application/rss+xml href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hugo/index.xml><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hugo/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hugo/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Hugo"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta name=twitter:card content="summary"><meta name=twitter:title content="Hugo"><meta name=twitter:description content></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><header class=page-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/>Tags</a></div><h1>Hugo
<a href=/tags/hugo/index.xml title=RSS aria-label=RSS><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" height="23"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg></a></h1></header><article class="post-entry tag-entry"><header class=entry-header><h2 class=entry-hint-parent>Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod</h2></header><div class=entry-content><p>TL;DR — The site is built once (Hugo project), published twice:
Onion: http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ via Tor, no TLS/HSTS, bound to 127.0.0.1:3301. Clearnet: https://blog.alipourimjourneys.ir behind Cloudflare, Lets Encrypt cert, Onion-Location header pointing to the onion mirror. 1) Tor hidden service (onion) basics I used Tors v3 onion services and mapped onion port 80 → my local web server on 127.0.0.1:3301.
Install & configure Tor (Debian/Ubuntu):
sudo apt update && sudo apt install -y tor sudoedit /etc/tor/torrc Add:
...</p></div><footer class=entry-footer><span title='2025-09-19 00:00:00 +0000 UTC'>September 19, 2025</span>&nbsp;·&nbsp;6 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/#isso-thread class=isso-comments-link>Comments</a></span></footer><a class=entry-link aria-label="post link to Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod" href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/></a></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><script src=/isso/js/count.min.js data-isso=/isso async></script><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>

422
public/tags/hugo/index.xml Normal file
View file

@ -0,0 +1,422 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Hugo on AlipourIm journeys</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hugo/</link><description>Recent content in Hugo on AlipourIm journeys</description><generator>Hugo -- 0.146.0</generator><language>en</language><lastBuildDate>Fri, 19 Sep 2025 00:00:00 +0000</lastBuildDate><atom:link href="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hugo/index.xml" rel="self" type="application/rss+xml"/><item><title>Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod</title><link>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/</link><pubDate>Fri, 19 Sep 2025 00:00:00 +0000</pubDate><guid>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/</guid><description>How I hosted a Hugo + PaperMod site both as a Tor onion service and a normal HTTPS site behind Cloudflare, with clean configs, dual builds, and a one-command deploy.</description><content:encoded><![CDATA[<blockquote>
<p>TL;DR — The site is built once (Hugo project), <strong>published twice</strong>:</p>
<ul>
<li><strong>Onion</strong>: <code>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/</code> via Tor, no TLS/HSTS, bound to <code>127.0.0.1:3301</code>.</li>
<li><strong>Clearnet</strong>: <code>https://blog.alipourimjourneys.ir</code> behind Cloudflare, Lets Encrypt cert, <code>Onion-Location</code> header pointing to the onion mirror.</li>
</ul></blockquote>
<hr>
<h2 id="1-tor-hidden-service-onion-basics">1) Tor hidden service (onion) basics</h2>
<p>I used Tors v3 onion services and mapped onion port 80 → my local web server on <code>127.0.0.1:3301</code>.</p>
<p><strong>Install &amp; configure Tor (Debian/Ubuntu):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt update <span style="color:#f92672">&amp;&amp;</span> sudo apt install -y tor
</span></span><span style="display:flex;"><span>sudoedit /etc/tor/torrc
</span></span></code></pre></div><p>Add:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 344 41"
>
<g transform='translate(8,16)'>
<path d='M 132,8 L 124,40' fill='none' stroke='currentColor'></path>
<path d='M 196,8 L 188,40' fill='none' stroke='currentColor'></path>
<path d='M 228,8 L 220,40' fill='none' stroke='currentColor'></path>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>8</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>7</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>/</text>
</g>
</svg>
</div>
<p>Make sure the directory is owned by Tors user and private:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo mkdir -p /var/lib/tor/hidden_site
</span></span><span style="display:flex;"><span>sudo chown -R debian-tor:debian-tor /var/lib/tor/hidden_site
</span></span><span style="display:flex;"><span>sudo chmod <span style="color:#ae81ff">700</span> /var/lib/tor/hidden_site
</span></span></code></pre></div><p><strong>Important:</strong> use the right systemd unit:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># validate as the Tor user</span>
</span></span><span style="display:flex;"><span>sudo -u debian-tor tor -f /etc/tor/torrc --verify-config
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># (re)start the real service</span>
</span></span><span style="display:flex;"><span>sudo systemctl enable --now tor@default
</span></span><span style="display:flex;"><span>sudo systemctl restart tor@default
</span></span></code></pre></div><p>Get the onion address:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo cat /var/lib/tor/hidden_site/hostname
</span></span></code></pre></div><p>Quick check (do remote DNS via SOCKS):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -I --socks5-hostname 127.0.0.1:9050 <span style="color:#e6db74">&#34;http://</span><span style="color:#66d9ef">$(</span>sudo cat /var/lib/tor/hidden_site/hostname<span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span>
</span></span></code></pre></div><hr>
<h2 id="2-nginx-for-the-onion-localhost-only">2) Nginx for the onion (localhost-only)</h2>
<p>I keep the onion site strictly on localhost: <strong>no HTTPS, no redirects, no HSTS</strong>. Tor already provides e2e encryption and authenticity.</p>
<p><code>/etc/nginx/sites-available/onion-blog</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> 127.0.0.1:<span style="color:#ae81ff">3301</span> <span style="color:#e6db74">default_server</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">&lt;your-56-char&gt;.onion</span> 127.0.0.1 <span style="color:#e6db74">localhost</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># keep onion simple; no HSTS/redirects here
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Referrer-Policy</span> <span style="color:#e6db74">no-referrer</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">X-Content-Type-Options</span> <span style="color:#e6db74">nosniff</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">X-Frame-Options</span> <span style="color:#e6db74">SAMEORIGIN</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># (optional) strict CSP so nothing leaks to clearnet
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e"># add_header Content-Security-Policy &#34;default-src &#39;self&#39;; img-src &#39;self&#39; data:; style-src &#39;self&#39; &#39;unsafe-inline&#39;; script-src &#39;self&#39;&#34; always;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/srv/hugo/mysite/public-onion</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">index</span> <span style="color:#e6db74">index.html</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> { <span style="color:#f92672">try_files</span> $uri $uri/ <span style="color:#e6db74">/index.html</span>; }
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> ~<span style="color:#e6db74">*</span> <span style="color:#e6db74">\.(css|js|ico|png|jpg|jpeg|gif|svg|webp|txt|xml)</span>$ {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">access_log</span> <span style="color:#66d9ef">off</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Cache-Control</span> <span style="color:#e6db74">&#34;public,</span> <span style="color:#e6db74">max-age=31536000,</span> <span style="color:#e6db74">immutable&#34;</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">try_files</span> $uri =<span style="color:#ae81ff">404</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Enable/reload:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo ln -sf /etc/nginx/sites-available/onion-blog /etc/nginx/sites-enabled/onion-blog
</span></span><span style="display:flex;"><span>sudo nginx -t <span style="color:#f92672">&amp;&amp;</span> sudo systemctl reload nginx
</span></span></code></pre></div><blockquote>
<p>Gotcha I hit: I had <strong>two</strong> server blocks on <code>127.0.0.1:3301</code>, which caused 404s via onion. Make sure only the intended vhost listens there (or mark it <code>default_server</code>). Also, if you ever use a regex in <code>server_name</code>, the syntax is <code>server_name ~* \.onion$</code> (note the space after <code>~*</code>).</p></blockquote>
<hr>
<h2 id="3-hugo--papermod-setup-and-version-bumps">3) Hugo + PaperMod setup (and version bumps)</h2>
<p>PaperMod now requires <strong>Hugo Extended ≥ 0.146.0</strong>. I installed the extended binary from the official tarball to avoid Snaps sandbox limitations (Snap cant read <code>/srv</code> paths by default).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># install Hugo extended (example)</span>
</span></span><span style="display:flex;"><span>VER<span style="color:#f92672">=</span>0.146.0
</span></span><span style="display:flex;"><span>cd /tmp
</span></span><span style="display:flex;"><span>wget https://github.com/gohugoio/hugo/releases/download/v<span style="color:#e6db74">${</span>VER<span style="color:#e6db74">}</span>/hugo_extended_<span style="color:#e6db74">${</span>VER<span style="color:#e6db74">}</span>_Linux-amd64.tar.gz
</span></span><span style="display:flex;"><span>tar -xzf hugo_extended_<span style="color:#e6db74">${</span>VER<span style="color:#e6db74">}</span>_Linux-amd64.tar.gz
</span></span><span style="display:flex;"><span>sudo mv hugo /usr/local/bin/hugo
</span></span><span style="display:flex;"><span>hugo version <span style="color:#75715e"># should say &#34;extended&#34; and &gt;= 0.146.0</span>
</span></span></code></pre></div><p>Create the site and theme:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo mkdir -p /srv/hugo <span style="color:#f92672">&amp;&amp;</span> sudo chown -R <span style="color:#e6db74">&#34;</span>$USER<span style="color:#e6db74">&#34;</span>:<span style="color:#e6db74">&#34;</span>$USER<span style="color:#e6db74">&#34;</span> /srv/hugo
</span></span><span style="display:flex;"><span>cd /srv/hugo
</span></span><span style="display:flex;"><span>hugo new site mysite
</span></span><span style="display:flex;"><span>cd mysite
</span></span><span style="display:flex;"><span>git init
</span></span><span style="display:flex;"><span>git submodule add https://github.com/adityatelange/hugo-PaperMod themes/PaperMod
</span></span></code></pre></div><p><strong>Config updates:</strong> in newer Hugo, <code>paginate</code> is deprecated → use:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span><span style="color:#75715e"># config/_default/hugo.toml</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">title</span> = <span style="color:#e6db74">&#34;My Blog&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">theme</span> = <span style="color:#e6db74">&#34;PaperMod&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">enableRobotsTXT</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">pagination</span>]
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">pagerSize</span> = <span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">params</span>]
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">defaultTheme</span> = <span style="color:#e6db74">&#34;auto&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showReadingTime</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showPostNavLinks</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showBreadCrumbs</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">showCodeCopyButtons</span> = <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><p>I added per-environment overrides so I can build two outputs with different <code>baseURL</code>s:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span><span style="color:#75715e"># config/clearnet/hugo.toml</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">baseURL</span> = <span style="color:#e6db74">&#34;https://blog.alipourimjourneys.ir/&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># config/onion/hugo.toml</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">baseURL</span> = <span style="color:#e6db74">&#34;http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/&#34;</span>
</span></span></code></pre></div><hr>
<h2 id="4-dual-builds-clearnet--onion-and-one-command-deploy">4) Dual builds (clearnet + onion) and one-command deploy</h2>
<p>I publish the same content twice—once for each base URL and docroot:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd /srv/hugo/mysite
</span></span><span style="display:flex;"><span><span style="color:#75715e"># clearnet build (served over HTTPS)</span>
</span></span><span style="display:flex;"><span>hugo --minify --environment clearnet -d public-clearnet
</span></span><span style="display:flex;"><span><span style="color:#75715e"># onion build (served via Tor)</span>
</span></span><span style="display:flex;"><span>hugo --minify --environment onion -d public-onion
</span></span><span style="display:flex;"><span>sudo systemctl reload nginx
</span></span></code></pre></div><p>Helper script I use:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo tee /usr/local/bin/build-both &gt;/dev/null <span style="color:#e6db74">&lt;&lt;&#39;EOF&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">set -euo pipefail
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">cd /srv/hugo/mysite
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">hugo --minify --environment clearnet -d public-clearnet
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">hugo --minify --environment onion -d public-onion
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">sudo systemctl reload nginx
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">echo &#34;Deployed both at $(date)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span>sudo chmod +x /usr/local/bin/build-both
</span></span></code></pre></div><p>While editing, I sometimes auto-rebuild on file save:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt install -y entr
</span></span><span style="display:flex;"><span>cd /srv/hugo/mysite
</span></span><span style="display:flex;"><span>find content layouts assets static config -type f | entr -r build-both
</span></span></code></pre></div><hr>
<h2 id="5-clearnet-behind-cloudflare--lets-encrypt-manual-dns-01">5) Clearnet behind Cloudflare + Lets Encrypt (manual DNS-01)</h2>
<p>Cloudflare is set to <strong>Full (strict)</strong>. I issued a public cert for <code>blog.alipourimjourneys.ir</code> using <strong>manual DNS-01</strong> (no API token):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo snap install --classic certbot
</span></span><span style="display:flex;"><span>sudo certbot certonly --manual --preferred-challenges dns -d blog.alipourimjourneys.ir --agree-tos -m you@example.com --no-eff-email
</span></span></code></pre></div><p>Certbot tells you to add a TXT record <code>_acme-challenge.blog.alipourimjourneys.ir</code>. Add it in Cloudflare DNS, verify with <code>dig</code>, then continue. Cert ends up at:</p>
<div class="goat svg-container ">
<svg
xmlns="http://www.w3.org/2000/svg"
font-family="Menlo,Lucida Console,monospace"
viewBox="0 0 496 41"
>
<g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='280' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='288' y='20' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='296' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='304' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='312' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='320' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='336' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='344' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='352' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='360' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='368' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='376' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='384' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='392' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='400' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='408' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='416' y='20' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='424' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='432' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='440' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='448' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='456' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='464' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>m</text>
</g>
</svg>
</div>
<p>Clearnet Nginx vhosts:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#75715e"># HTTP → HTTPS redirect (optional; CF usually talks HTTPS to origin anyway)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">80</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:80</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">blog.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">return</span> <span style="color:#ae81ff">301</span> <span style="color:#e6db74">https://blog.alipourimjourneys.ir</span>$request_uri;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># HTTPS origin (behind Cloudflare)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>; <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">blog.alipourimjourneys.ir</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/blog.alipourimjourneys.ir/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/blog.alipourimjourneys.ir/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># HSTS on clearnet only
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Strict-Transport-Security</span> <span style="color:#e6db74">&#34;max-age=31536000</span>; <span style="color:#f92672">includeSubDomains</span>; <span style="color:#f92672">preload&#34;</span> <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e"># Help Tor Browser discover the onion mirror (safe on clearnet)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">add_header</span> <span style="color:#e6db74">Onion-Location</span> <span style="color:#e6db74">&#34;http://&lt;your-56-char&gt;.onion</span>$request_uri&#34; <span style="color:#e6db74">always</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/srv/hugo/mysite/public-clearnet</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">index</span> <span style="color:#e6db74">index.html</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> { <span style="color:#f92672">try_files</span> $uri $uri/ <span style="color:#e6db74">/index.html</span>; }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>Note: dont add HSTS or HTTPS redirects to the <strong>onion</strong> vhost. Keep onion pure HTTP on localhost.</p></blockquote>
<hr>
<h2 id="6-troubleshooting-i-ran-into-and-fixes">6) Troubleshooting I ran into (and fixes)</h2>
<ul>
<li><strong>Tor unit confusion:</strong> <code>tor.service</code> is a tiny master; the real daemon is <code>tor@default</code>. Use that unit and verify config as <code>debian-tor</code>.</li>
<li><strong>Permissions:</strong> <code>HiddenServiceDir</code> must be owned by <code>debian-tor</code> and mode <code>700</code>.</li>
<li><strong>Mapping mismatch:</strong> If <code>HiddenServicePort 80 127.0.0.1:3301</code> is set, visit <code>http://&lt;onion&gt;/</code> (no <code>:3301</code>). If you set <code>HiddenServicePort 3301 127.0.0.1:3301</code>, you must use <code>http://&lt;onion&gt;:3301/</code>.</li>
<li><strong>Curl &amp; .onion:</strong> modern curl refuses <code>.onion</code> unless you use remote DNS via SOCKS:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -I --socks5-hostname 127.0.0.1:9050 <span style="color:#e6db74">&#34;http://&lt;onion&gt;/&#34;</span>
</span></span></code></pre></div></li>
<li><strong>Two Nginx vhosts on the same port:</strong> I had a duplicate server on <code>127.0.0.1:3301</code> pointing somewhere else, which caused onion 404s. Keep only one (or mark one <code>default_server</code>).</li>
<li><strong>Regex in <code>server_name</code>:</strong> if you use it, write <code>server_name ~* \.onion$</code> (space after <code>~*</code>). I fixed an <code>invalid variable name</code> error caused by a missing space.</li>
<li><strong>PaperMod with old Hugo:</strong> upgraded to <strong>extended ≥ 0.146.0</strong>. Also updated <code>paginate</code><code>[pagination].pagerSize</code>.</li>
<li><strong>Snap confinement:</strong> Snaps <code>hugo</code> couldnt read <code>/srv</code> (<code>.../void: permission denied</code>). Switched to the tarball build in <code>/usr/local/bin</code>.</li>
</ul>
<hr>
<h2 id="7-not-secure-in-tor-browser">7) “Not secure” in Tor Browser?</h2>
<p>That message can appear because onion uses <strong>HTTP</strong>. Its OK: Tor provides e2e encryption + onion auth. If you enable HTTPS-Only Mode, add an exception for the site. Also ensure the onion build doesnt reference <strong>clearnet</strong> resources (scan the built HTML for <code>http(s)://</code> links that arent your onion).</p>
<hr>
<h2 id="8-day-to-day-workflow">8) Day-to-day workflow</h2>
<ul>
<li>Create content:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>hugo new posts/my-first-post.md <span style="color:#75715e"># then set draft: false</span>
</span></span></code></pre></div></li>
<li>Publish both:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>build-both
</span></span></code></pre></div></li>
<li>(Optional) Auto on save:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>find content layouts assets static config -type f | entr -r build-both
</span></span></code></pre></div></li>
<li>(Optional) Git push-to-deploy with a bare repo + post-receive hook that runs <code>build-both</code>.</li>
</ul>
<hr>
<h2 id="final-notes">Final notes</h2>
<ul>
<li>Clearnet gets <strong>HTTPS + HSTS</strong> and an <code>Onion-Location</code> header.</li>
<li>Onion gets <strong>no HSTS/redirects</strong>, and all assets are self-hosted to avoid mixed content.</li>
<li>Serving both worlds from one Hugo repo is easy: <strong>two builds, two vhosts, one workflow</strong>.</li>
</ul>
]]></content:encoded></item></channel></rss>

View file

@ -0,0 +1,2 @@
<!doctype html><html lang=en><head><title>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hugo/</title>
<link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hugo/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hugo/"></head></html>

Some files were not shown because too many files have changed in this diff Show more