redesign: irc-client / terminal aesthetic — buffer, nicklist, cap-ls grammar

This commit is contained in:
Jean Chevronnet 2026-08-30 18:36:41 +00:00
parent 899040a319
commit 182f14a7f2
No known key found for this signature in database
GPG key ID: 439666D63A9477E4
6 changed files with 264 additions and 321 deletions

View file

@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<rect width="64" height="64" rx="14" fill="#0b0e14"/> <rect width="64" height="64" rx="13" fill="#0d0e12"/>
<text x="50%" y="55%" font-family="ui-monospace,monospace" font-size="34" font-weight="700" <text x="13" y="45" font-family="ui-monospace,monospace" font-size="33" font-weight="700" fill="#e2a25a">&#8250;</text>
fill="#e6edf3" text-anchor="middle" dominant-baseline="middle">e<tspan fill="#3fb950">.</tspan></text> <rect x="37" y="27" width="13" height="20" rx="1" fill="#c8ccd4"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 329 B

After

Width:  |  Height:  |  Size: 315 B

Before After
Before After

View file

@ -1,124 +1,126 @@
/* echoIRCd — the site as an IRC client. Monospace, warm phosphor, real IRC grammar. */
:root{ :root{
--bg:#0b0e14; --bg2:#11161f; --bg3:#161c27; --bg:#08090c; --win:#0d0e12; --panel:#14161c; --panel2:#0f1116;
--line:#222b38; --line2:#2b3646; --edge:#22252e; --edge2:#2d313c;
--fg:#c8d3e0; --fg2:#7d8896; --hi:#e6edf3; --fg:#c8ccd4; --dim:#767b85; --dimmer:#4a4e58;
--accent:#3fb950; --accent2:#2ea043; --link:#58a6ff; --amber:#e2a25a; --green:#5fbf6b; --blue:#5f9bef; --magenta:#c58af0; --cyan:#4fcccf; --red:#ec6d6d;
--radius:12px; --wrap:1040px; --link:#6aa6f2; --ok:#5fbf6b;
color-scheme:dark; --mono:ui-monospace,"SF Mono","JetBrains Mono","Cascadia Code",Menlo,Consolas,"DejaVu Sans Mono",monospace;
} }
*{box-sizing:border-box} *{box-sizing:border-box}
html{scroll-behavior:smooth} html{-webkit-text-size-adjust:100%}
body{ body{
margin:0; background:var(--bg); color:var(--fg); margin:0;background:var(--bg);color:var(--fg);
font:16px/1.65 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; font:14.5px/1.65 var(--mono);
background-image:radial-gradient(1200px 600px at 50% -10%,rgba(226,162,90,.05),transparent 70%);
-webkit-font-smoothing:antialiased; -webkit-font-smoothing:antialiased;
} }
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
a{color:var(--link);text-decoration:none} a{color:var(--link);text-decoration:none}
a:hover{text-decoration:underline} a:hover{text-decoration:underline}
h1,h2,h3,h4{color:var(--hi);line-height:1.2} b{color:var(--fg);font-weight:600}
.accent{color:var(--accent)} .dim{color:var(--dim)}
main{min-height:60vh} .kw{color:var(--amber)}
.wrap,.hero,.split,.page-head,.cs-in{max-width:var(--wrap);margin:0 auto;padding-left:1.25rem;padding-right:1.25rem} .ok{color:var(--ok)}
.i{font-style:italic}
.n-amber{color:var(--amber)} .n-green{color:var(--green)} .n-blue{color:var(--blue)}
.n-magenta{color:var(--magenta)} .n-cyan{color:var(--cyan)} .n-red{color:var(--red)}
/* nav */ /* the client window */
.nav{ .client{
display:flex;align-items:center;justify-content:space-between;gap:1rem; max-width:1060px;margin:1.6rem auto;background:var(--win);
max-width:var(--wrap);margin:0 auto;padding:1.1rem 1.25rem; border:1px solid var(--edge);border-radius:9px;overflow:hidden;
} box-shadow:0 24px 60px -30px rgba(0,0,0,.8),0 0 0 1px rgba(255,255,255,.01);
.brand{font-family:ui-monospace,monospace;font-weight:700;font-size:1.25rem;color:var(--hi)}
.brand:hover{text-decoration:none}
.brand .dot{color:var(--accent)}
.navlinks{display:flex;gap:1.4rem;align-items:center;flex-wrap:wrap}
.navlinks a{color:var(--fg2);font-size:.95rem}
.navlinks a:hover{color:var(--hi);text-decoration:none}
.navlinks a.on{color:var(--hi)}
.navlinks a.ext{color:var(--fg2)}
/* hero */
.hero{padding:3.5rem 1.25rem 2rem;text-align:center}
.badge-row{display:flex;gap:.5rem;justify-content:center;flex-wrap:wrap;margin-bottom:1.6rem}
.badge{
font-size:.78rem;color:var(--accent);background:rgba(63,185,80,.08);
border:1px solid rgba(63,185,80,.28);border-radius:999px;padding:.25rem .7rem;
}
.hero h1{font-size:clamp(2.1rem,5vw,3.4rem);letter-spacing:-1.5px;margin:.2rem 0 1rem}
.lede{color:var(--fg2);font-size:1.12rem;max-width:640px;margin:0 auto 1.8rem}
.cta{display:flex;gap:.8rem;justify-content:center;flex-wrap:wrap}
.btn{
display:inline-block;padding:.65rem 1.2rem;border-radius:8px;font-weight:600;
border:1px solid var(--line2);color:var(--hi);background:var(--bg2);
}
.btn:hover{border-color:#3a475a;text-decoration:none}
.btn.primary{background:var(--accent2);border-color:var(--accent);color:#04120a}
.btn.primary:hover{background:var(--accent)}
.connectline{
margin:1.8rem auto 0;max-width:max-content;color:var(--accent);
background:var(--bg2);border:1px solid var(--line);border-radius:8px;padding:.55rem 1rem;font-size:.95rem;
} }
/* split cards */ /* title bar */
.split{display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-top:1.5rem} .titlebar{display:flex;align-items:center;gap:.6rem;padding:.55rem .9rem;background:var(--panel);border-bottom:1px solid var(--edge)}
.card{background:var(--bg2);border:1px solid var(--line);border-radius:var(--radius);padding:1.4rem 1.5rem} .conn{width:9px;height:9px;border-radius:50%;background:var(--green);box-shadow:0 0 8px var(--green);flex:none}
.card h3{margin:0 0 .5rem;font-size:1.25rem} .tb-name{color:var(--fg);font-weight:600}
.card p{margin:0;color:var(--fg2)} .tb-server{margin-left:auto;color:var(--dim);font-size:.82rem}
.tag{font-size:.7rem;text-transform:uppercase;letter-spacing:.1em;color:var(--fg2);
border:1px solid var(--line2);border-radius:6px;padding:.1rem .45rem;margin-left:.4rem;vertical-align:middle}
/* feature grid */ /* tabs */
.wrap{padding-top:3.5rem;padding-bottom:1rem} .tabs{display:flex;gap:.15rem;padding:0 .5rem;background:var(--panel);border-bottom:1px solid var(--edge);overflow-x:auto}
.section-title{text-align:center;font-size:1.6rem;margin:0 0 2rem} .tab{padding:.5rem .8rem;color:var(--dim);border-bottom:2px solid transparent;white-space:nowrap}
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1rem} .tab:hover{color:var(--fg);text-decoration:none}
.feat{background:var(--bg2);border:1px solid var(--line);border-radius:var(--radius);padding:1.3rem 1.4rem} .tab.on{color:var(--amber);border-bottom-color:var(--amber)}
.feat .ico{font-size:1.5rem;margin-bottom:.5rem} .tab.ext{margin-left:auto;color:var(--dimmer)}
.feat h4{margin:.1rem 0 .5rem;font-size:1.08rem}
.feat p{margin:0;color:var(--fg2);font-size:.95rem}
.feat em{color:var(--fg);font-style:normal;font-weight:600}
/* connect strip */ /* topic */
.connect-strip{margin:4rem 0 0;background:linear-gradient(180deg,var(--bg2),var(--bg3));border-top:1px solid var(--line)} .topic{padding:.5rem .95rem;background:var(--panel2);border-bottom:1px solid var(--edge);color:var(--dim);font-size:.85rem}
.cs-in{display:flex;align-items:center;justify-content:space-between;gap:1.5rem;padding:2.4rem 1.25rem;flex-wrap:wrap} .topic .tp{color:var(--amber);margin-right:.35rem}
.cs-in h2{margin:0 0 .2rem;font-size:1.5rem}
.cs-in p{margin:0;color:var(--fg2)}
.mini{border-collapse:collapse;font-size:.95rem}
.mini td{padding:.28rem .9rem;border-bottom:1px solid var(--line)}
.mini td:first-child{color:var(--fg2)}
/* pages */ /* workspace = buffer + nicklist */
.page-head{padding:3rem 1.25rem 1rem;text-align:center} .workspace{display:flex;align-items:stretch}
.page-head h1{font-size:2.4rem;letter-spacing:-1px;margin:0 0 .6rem} .buffer{flex:1;min-width:0;padding:1.1rem 1.1rem 1.4rem}
.prose .block{margin:0 0 2.2rem} .nicklist{width:13.5rem;flex:none;background:var(--panel2);border-left:1px solid var(--edge);padding:1rem .85rem;font-size:.86rem}
.block h2{font-size:1.35rem;border-bottom:1px solid var(--line);padding-bottom:.5rem;margin:0 0 .9rem} .nl-head{color:var(--dim);text-transform:uppercase;letter-spacing:.12em;font-size:.72rem;margin-bottom:.6rem}
.block ul{margin:.4rem 0 0;padding-left:1.1rem} .nicklist ul{list-style:none;margin:0;padding:0}
.block li{margin:.35rem 0;color:var(--fg2)} .nicklist li{display:flex;align-items:center;gap:.15rem;padding:.14rem 0;white-space:nowrap}
.block li strong{color:var(--fg)} .nicklist .s{display:inline-block;width:1.1em;text-align:center;color:var(--dim)}
.bf{margin-left:.35rem;font-size:.62rem;color:var(--dimmer);border:1px solid var(--edge2);border-radius:3px;padding:0 .25em}
/* chips */ /* buffer lines — [time] [gutter] [message] */
.chips{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:.6rem} .ln{display:grid;grid-template-columns:3.1em 7em 1fr;column-gap:.75em;align-items:baseline;padding:.09rem 0}
.chips span{ .ln .t{color:var(--dimmer);font-size:.82em}
font-family:ui-monospace,monospace;font-size:.8rem;color:var(--fg); .ln .g{text-align:right;white-space:nowrap;color:var(--dim)}
background:var(--bg3);border:1px solid var(--line);border-radius:6px;padding:.2rem .5rem; .ln .m{min-width:0;word-wrap:break-word;overflow-wrap:anywhere}
} .ln .m a{color:var(--link)}
/* tables + code on connect page */ /* banner / wordmark */
.detail{width:100%;border-collapse:collapse;margin:.5rem 0 1rem} .banner{padding:.6rem 0 1.4rem;border-bottom:1px dashed var(--edge);margin-bottom:1rem}
.detail th{text-align:left;color:var(--fg2);font-weight:500;padding:.5rem .9rem;border-bottom:1px solid var(--line);white-space:nowrap;vertical-align:top} .wordmark{font-size:clamp(2.3rem,7vw,3.7rem);font-weight:700;letter-spacing:-2px;color:var(--fg);line-height:1}
.detail td{padding:.5rem .9rem;border-bottom:1px solid var(--line);color:var(--fg)} .wordmark .w-hi{color:var(--amber)}
.code{ .w-sub{color:var(--dim);margin-top:.5rem;font-size:.95rem}
background:var(--bg2);border:1px solid var(--line);border-radius:8px; .cur{display:inline-block;width:.55em;color:var(--amber);animation:blink 1.1s steps(1) infinite}
padding:1rem 1.1rem;overflow-x:auto;color:var(--accent); .cur-lg{width:.5em;margin-left:.06em}
font-family:ui-monospace,monospace;font-size:.9rem;line-height:1.7;white-space:pre; @keyframes blink{50%{opacity:0}}
} @media(prefers-reduced-motion:reduce){.cur{animation:none}}
.hint{color:var(--fg2);font-size:.92rem}
.webchat{display:flex;align-items:center;gap:1rem;flex-wrap:wrap;margin:1.5rem 0 0;color:var(--fg2)} /* quickfacts */
.quickfacts{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:1.3rem}
/* footer */ .qf{border:1px solid var(--edge);border-radius:6px;padding:.4rem .7rem;color:var(--dim);background:var(--panel2);font-size:.85rem}
.foot{border-top:1px solid var(--line);margin-top:4rem} .qf:hover{border-color:var(--edge2);color:var(--fg);text-decoration:none}
.foot-in{max-width:var(--wrap);margin:0 auto;padding:1.6rem 1.25rem;display:flex;justify-content:space-between;gap:1rem;flex-wrap:wrap;color:var(--fg2);font-size:.9rem} .qf .qf-k{color:var(--amber)}
.foot .mono{color:var(--fg)}
.foot-links{display:flex;gap:1.1rem} /* feature groups */
.grp{margin:1.5rem 0}
@media(max-width:760px){ .grp-h{color:var(--amber);font-size:.82rem;text-transform:uppercase;letter-spacing:.14em;padding-bottom:.4rem;margin-bottom:.7rem;border-bottom:1px solid var(--edge);position:relative}
.split{grid-template-columns:1fr} .grp-h::before{content:"── ";color:var(--dimmer)}
.grid{grid-template-columns:1fr} .bul{list-style:none;margin:0;padding:0}
.bul li{padding:.22rem 0 .22rem 1.3em;position:relative;color:var(--fg)}
.bul li::before{content:"·";position:absolute;left:.3em;color:var(--amber)}
.srvline{display:grid;grid-template-columns:7em 1fr;column-gap:.75em;align-items:baseline;margin:.2rem 0 .8rem}
.srvline .g{text-align:right}
.capline{color:var(--dim);word-break:break-word;line-height:1.7}
/* connect page: tables + code */
.detail{width:100%;border-collapse:collapse;margin:.7rem 0 1rem;font-size:.9rem}
.detail th{text-align:left;color:var(--dim);font-weight:400;padding:.4rem .8rem .4rem 0;border-bottom:1px solid var(--edge);white-space:nowrap;vertical-align:top;width:1%}
.detail td{padding:.4rem 0;border-bottom:1px solid var(--edge);color:var(--fg)}
.note{color:var(--dim);margin:.2rem 0 .6rem;font-size:.9rem}
.code{background:var(--panel2);border:1px solid var(--edge);border-left:2px solid var(--amber);border-radius:6px;
padding:.8rem 1rem;overflow-x:auto;color:var(--green);font-size:.88rem;line-height:1.75;white-space:pre;margin:.4rem 0}
.webchat{display:flex;align-items:center;gap:.8rem;flex-wrap:wrap;margin:1.1rem 0}
.btn{display:inline-block;border:1px solid var(--edge2);background:var(--panel);color:var(--fg);padding:.4rem .85rem;border-radius:6px;font-size:.88rem}
.btn:hover{border-color:var(--amber);color:var(--amber);text-decoration:none}
/* input + status bars */
.inputbar{display:flex;align-items:baseline;gap:.5rem;padding:.55rem .95rem;background:var(--panel);border-top:1px solid var(--edge);font-size:.9rem;overflow:hidden}
.ib-ch{color:var(--dim)}
.ib-prompt{color:var(--amber)}
.ib-text{color:var(--fg);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.statusbar{display:flex;align-items:center;gap:.8rem;padding:.4rem .95rem;background:var(--panel2);border-top:1px solid var(--edge);font-size:.74rem;color:var(--dim)}
.statusbar .sb-m{margin:0 auto;color:var(--amber);opacity:.75}
.statusbar a{color:var(--dim)}
.statusbar a:hover{color:var(--fg)}
@media(max-width:720px){
.client{margin:0;border:0;border-radius:0;min-height:100vh}
.workspace{flex-direction:column}
.nicklist{width:auto;border-left:0;border-top:1px solid var(--edge);order:2}
.nicklist ul{display:flex;flex-wrap:wrap;gap:.1rem .9rem}
.ln{grid-template-columns:5.5em 1fr}
.ln .t{display:none}
.srvline{grid-template-columns:5.5em 1fr}
.tb-server{display:none}
} }

View file

@ -3,38 +3,67 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark">
<title>{% block title %}echoIRCd{% endblock %}</title> <title>{% block title %}echoIRCd{% endblock %}</title>
<meta name="description" content="echoIRCd — a modern IRC network built from scratch in Rust: TLS 1.3, full IRCv3, SASL, and native services."> <meta name="description" content="echoIRCd — an IRC network written from scratch in Rust. Full IRCv3, modern TLS, native services.">
<meta property="og:title" content="echoIRCd"> <meta property="og:title" content="echoIRCd">
<meta property="og:description" content="A modern IRC network, built from scratch in Rust."> <meta property="og:description" content="An IRC network written from scratch in Rust.">
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<link rel="icon" href="/favicon.svg" type="image/svg+xml"> <link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="/static/style.css"> <link rel="stylesheet" href="/static/style.css">
</head> </head>
<body> <body>
<header class="nav"> <div class="client">
<a class="brand" href="/">echo<span class="dot">IRCd</span></a>
<nav class="navlinks">
<a href="/"{% if active == "home" %} class="on"{% endif %}>Home</a>
<a href="/features"{% if active == "features" %} class="on"{% endif %}>Features</a>
<a href="/connect"{% if active == "connect" %} class="on"{% endif %}>Connect</a>
<a href="https://git.devtronic.pro/echo/echoIRCd" class="ext">Source&nbsp;&#8599;</a>
</nav>
</header>
<main> <div class="titlebar">
{% block content %}{% endblock %} <span class="conn" title="connected"></span>
</main> <span class="tb-name">echoircd.org</span>
<span class="tb-server">irc.echoircd.org · +6697 · TLS1.3</span>
<footer class="foot">
<div class="foot-in">
<div>echoIRCd — native Rust IRC daemon &amp; services · <span class="mono">#![forbid(unsafe_code)]</span></div>
<div class="foot-links">
<a href="https://git.devtronic.pro/echo/echoIRCd">echoIRCd</a>
<a href="https://git.devtronic.pro/echo/echo">services</a>
<a href="https://git.devtronic.pro/echo/website">website</a>
</div>
</div> </div>
</footer>
<nav class="tabs">
<a href="/" class="tab{% if active == "home" %} on{% endif %}">#home</a>
<a href="/features" class="tab{% if active == "features" %} on{% endif %}">#features</a>
<a href="/connect" class="tab{% if active == "connect" %} on{% endif %}">#connect</a>
<a href="https://git.devtronic.pro/echo/echoIRCd" class="tab ext">git&#8599;</a>
</nav>
<div class="topic"><span class="tp">»</span> {% block topic %}echoIRCd — a native-Rust IRC daemon &amp; services{% endblock %}</div>
<div class="workspace">
<div class="buffer">
{% block buffer %}{% endblock %}
</div>
<aside class="nicklist">
{% block nicklist %}
<div class="nl-head">names · 9</div>
<ul class="nl">
<li><span class="s n-amber">~</span><span class="n-amber">echoircd</span></li>
<li><span class="s n-green">@</span><span class="n-green">NickServ</span><span class="bf">B</span></li>
<li><span class="s n-blue">@</span><span class="n-blue">ChanServ</span><span class="bf">B</span></li>
<li><span class="s n-magenta">@</span><span class="n-magenta">OperServ</span><span class="bf">B</span></li>
<li><span class="s n-cyan">@</span><span class="n-cyan">MemoServ</span><span class="bf">B</span></li>
<li><span class="s n-blue">+</span><span>TLS1.3</span></li>
<li><span class="s n-blue">+</span><span>IRCv3</span></li>
<li><span class="s n-blue">+</span><span>SASL</span></li>
<li><span class="s dim">·</span><span class="dim">you</span></li>
</ul>
{% endblock %}
</aside>
</div>
<div class="inputbar">
<span class="ib-ch">[{% block chan %}#home{% endblock %}]</span>
<span class="ib-prompt"></span>
<span class="ib-text">{% block cmd %}/connect{% endblock %}</span><span class="cur"></span>
</div>
<div class="statusbar">
<span class="sb-l">echoIRCd 5.0.0</span>
<span class="sb-m">#![forbid(unsafe_code)]</span>
<span class="sb-r"><a href="https://git.devtronic.pro/echo/echo">services</a> · <a href="https://git.devtronic.pro/echo/website">website</a></span>
</div>
</div>
</body> </body>
</html> </html>

View file

@ -1,58 +1,54 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Connect — echoIRCd{% endblock %} {% block title %}#connect — echoIRCd{% endblock %}
{% block content %} {% block topic %}#connect — point any client at irc.echoircd.org, or use the browser{% endblock %}
<section class="page-head"> {% block chan %}#connect{% endblock %}
<h1>Connect</h1> {% block cmd %}/server irc.echoircd.org +6697{% endblock %}
<p class="lede">Point any IRC client at the network — or open it in your browser.</p> {% block buffer %}
</section> <div class="ln"><span class="t">19:33</span><span class="g n-cyan">&lt;you&gt;</span><span class="m">/server irc.echoircd.org +6697</span></div>
<div class="ln"><span class="t">19:33</span><span class="g dim">-!-</span><span class="m dim">connecting to irc.echoircd.org:6697 … TLS1.3 up (X25519MLKEM768)</span></div>
<div class="ln"><span class="t">19:33</span><span class="g dim">-!-</span><span class="m dim">Welcome to the echoiRCd IRC Network</span></div>
<section class="wrap">
<table class="detail"> <table class="detail">
<tr><th>Server</th><td class="mono">irc.echoircd.org</td></tr> <tr><th>server</th><td>irc.echoircd.org</td></tr>
<tr><th>TLS (recommended)</th><td class="mono">6697</td></tr> <tr><th>tls (recommended)</th><td>6697</td></tr>
<tr><th>Plaintext</th><td class="mono">6667</td></tr> <tr><th>plaintext</th><td>6667</td></tr>
<tr><th>Network</th><td class="mono">echoiRCd</td></tr> <tr><th>network</th><td>echoiRCd</td></tr>
</table> </table>
<div class="webchat"> <div class="webchat">
<span>Prefer the browser?</span> <span class="dim">prefer the browser?</span>
<a class="btn" href="https://orbit.devtronic.pro">Open the web client &#8599;</a> <a class="btn" href="https://orbit.devtronic.pro">open the web client &#8599;</a>
</div> </div>
<h2 class="section-title">Client quick-start</h2> <div class="grp">
<div class="block"> <div class="grp-h">client quick-start</div>
<p>Most clients accept a one-line server string. The leading <span class="mono">+</span> on the <p class="note">most clients take a one-line server string; the leading <span class="kw">+</span> means TLS.</p>
port means TLS.</p>
<pre class="code">/server irc.echoircd.org +6697 <pre class="code">/server irc.echoircd.org +6697
/join #echoircd</pre> /join #echoircd</pre>
<table class="detail"> <table class="detail">
<tr><th>HexChat</th><td class="mono">Network list → Add → irc.echoircd.org/+6697 → tick "Use SSL"</td></tr> <tr><th>HexChat</th><td>add irc.echoircd.org/+6697 · tick “Use SSL”</td></tr>
<tr><th>WeeChat</th><td class="mono">/server add echo irc.echoircd.org/6697 -tls</td></tr> <tr><th>WeeChat</th><td>/server add echo irc.echoircd.org/6697 -tls</td></tr>
<tr><th>irssi</th><td class="mono">/connect -tls irc.echoircd.org 6697</td></tr> <tr><th>irssi</th><td>/connect -tls irc.echoircd.org 6697</td></tr>
<tr><th>mIRC</th><td class="mono">/server irc.echoircd.org +6697</td></tr> <tr><th>mIRC</th><td>/server irc.echoircd.org +6697</td></tr>
</table> </table>
</div> </div>
<h2 class="section-title">Register your nick</h2> <div class="grp">
<div class="block"> <div class="grp-h">register your nick</div>
<p>Registering lets you keep your nickname, found channels, and log in with SASL.</p> <p class="note">keep your nick, found channels, and log in with SASL.</p>
<pre class="code">/msg NickServ REGISTER &lt;password&gt; &lt;email&gt; <pre class="code">/msg NickServ REGISTER &lt;password&gt; &lt;email&gt;
/msg NickServ IDENTIFY &lt;password&gt;</pre> /msg NickServ IDENTIFY &lt;password&gt;</pre>
</div> </div>
<h2 class="section-title">Log in with a key (ECDSA)</h2> <div class="grp">
<div class="block"> <div class="grp-h">log in with a key (ecdsa)</div>
<p>Instead of a password you can authenticate with a NIST&nbsp;P-256 key. The server sends a <p class="note">sign a server challenge with a NIST&nbsp;P-256 key instead of sending a password — nothing secret crosses the wire.</p>
challenge, your client signs it, and the signature is checked against the key on your <pre class="code"># generate a key, read its public half
account — nothing secret crosses the wire.</p>
<pre class="code"># generate a key and read its public half
ecdsatool keygen ~/.ecdsa.pem ecdsatool keygen ~/.ecdsa.pem
ecdsatool pubkey ~/.ecdsa.pem ecdsatool pubkey ~/.ecdsa.pem
# store it on your account, then use ECDSA-NIST256P-CHALLENGE for SASL # store it, then pick ECDSA-NIST256P-CHALLENGE for SASL
/msg NickServ SET PUBKEY &lt;printed-public-key&gt;</pre> /msg NickServ SET PUBKEY &lt;printed-public-key&gt;</pre>
<p class="hint">Point your client's SASL settings at the same key file and pick the <p class="note">point your client's SASL settings at the same key file.</p>
<span class="mono">ECDSA-NIST256P-CHALLENGE</span> mechanism.</p>
</div> </div>
</section>
{% endblock %} {% endblock %}

View file

@ -1,85 +1,66 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Features — echoIRCd{% endblock %} {% block title %}#features — echoIRCd{% endblock %}
{% block content %} {% block topic %}#features — what's inside the daemon &amp; services{% endblock %}
<section class="page-head"> {% block chan %}#features{% endblock %}
<h1>Features</h1> {% block cmd %}/msg echoircd help{% endblock %}
<p class="lede">Everything below is original, native Rust — the InspIRCd protocol and API are a {% block buffer %}
reference for wire compatibility, never a source of code.</p> <div class="ln"><span class="t">19:31</span><span class="g n-cyan">&lt;you&gt;</span><span class="m">/msg echoircd what can you do</span></div>
</section> <div class="ln"><span class="t">19:31</span><span class="g n-amber">&lt;echoircd&gt;</span><span class="m">plenty — here's the short tour. all of it is original, native Rust.</span></div>
<section class="wrap prose"> <div class="grp">
<div class="block"> <div class="grp-h">the daemon</div>
<h2>The daemon</h2> <ul class="bul">
<ul> <li><b>safe by construction</b><span class="kw">#![forbid(unsafe_code)]</span> across the tree, enforced in CI</li>
<li><strong>Safe by construction</strong><span class="mono">#![forbid(unsafe_code)]</span> across the whole tree, enforced in CI.</li> <li><b>reactor core</b> — single core thread + mio/epoll pool, zero-copy broadcast, parallel channel fan-out</li>
<li><strong>Reactor core</strong> — a single core thread with a mio/epoll reactor pool; zero-copy broadcast lines and parallel large-channel fan-out.</li> <li><b>full mode set</b> — every standard channel/user mode, host cloaking, custom prefixes</li>
<li><strong>Full mode set</strong> — every standard channel and user mode, host cloaking, and custom prefixes.</li> <li><b>operators</b> — a privilege model: command grants, named privs, per-type mode allow-list</li>
<li><strong>Operators</strong> — an InspIRCd-style privilege model: command grants, named privileges, and a usermode/chanmode allow-list per oper type.</li> <li><b>the edge</b> — connection classes, per-class flood/fakelag, RFC1413 ident, PROXY v1/v2</li>
<li><strong>Edge</strong> — connection classes, per-class flood / fakelag, RFC&nbsp;1413 ident, and PROXY protocol v1/v2 (with TLS TLVs).</li> <li><b>observability</b> — a metrics endpoint, JSON logging, native syslog</li>
<li><strong>Observability</strong> — a metrics endpoint, structured JSON logging, and native syslog.</li>
</ul> </ul>
</div> </div>
<div class="block"> <div class="grp">
<h2>Transport &amp; TLS</h2> <div class="grp-h">transport &amp; tls</div>
<ul> <ul class="bul">
<li><strong>6697</strong> direct TLS, <strong>7799</strong> WebSocket-over-TLS (wss), <strong>6667</strong> plaintext.</li> <li><b>6697</b> direct TLS · <b>7799</b> WebSocket (wss) · <b>6667</b> plaintext</li>
<li>Two interchangeable backends — <strong>OpenSSL</strong> and <strong>rustls</strong> — both serving TLS&nbsp;1.3.</li> <li>two backends — <b>OpenSSL</b> and <b>rustls</b> — both serving TLS 1.3</li>
<li>With OpenSSL&nbsp;3.5 the handshake negotiates post-quantum <span class="mono">X25519MLKEM768</span> key exchange.</li> <li>with OpenSSL 3.5 the handshake negotiates post-quantum <span class="kw">X25519MLKEM768</span></li>
<li>Per-host <strong>SNI</strong> certificates, reloaded live on rehash.</li> <li>per-host <b>SNI</b> certificates, reloaded live on rehash</li>
</ul> </ul>
</div> </div>
<div class="block"> <div class="grp">
<h2>IRCv3</h2> <div class="grp-h">ircv3</div>
<p>Capabilities advertised to clients:</p> <div class="srvline"><span class="g n-amber">&lt;echoircd&gt;</span><span class="capline">CAP * LS :server-time message-tags account-tag account-notify extended-join chghost multi-prefix away-notify invite-notify setname echo-message userhost-in-names batch labeled-response standard-replies extended-monitor draft/chathistory draft/event-playback draft/message-redaction draft/multiline draft/metadata-2 draft/read-marker draft/webpush draft/account-registration sts</span></div>
<div class="chips">
<span>server-time</span><span>message-tags</span><span>account-tag</span><span>account-notify</span>
<span>extended-join</span><span>chghost</span><span>multi-prefix</span><span>away-notify</span>
<span>invite-notify</span><span>setname</span><span>echo-message</span><span>userhost-in-names</span>
<span>batch</span><span>labeled-response</span><span>standard-replies</span><span>extended-monitor</span>
<span>draft/chathistory</span><span>draft/event-playback</span><span>draft/message-redaction</span>
<span>draft/multiline</span><span>draft/metadata-2</span><span>draft/read-marker</span>
<span>draft/webpush</span><span>draft/account-registration</span><span>sts</span>
</div>
</div> </div>
<div class="block"> <div class="grp">
<h2>SASL</h2> <div class="grp-h">sasl</div>
<ul> <div class="srvline"><span class="g n-amber">&lt;echoircd&gt;</span><span class="capline">sasl=PLAIN,EXTERNAL,SCRAM-SHA-256,ECDSA-NIST256P-CHALLENGE</span></div>
<li><strong>PLAIN</strong> — classic username/password.</li> <ul class="bul">
<li><strong>EXTERNAL</strong> — authenticate by TLS client-certificate fingerprint.</li> <li><b>EXTERNAL</b> — authenticate by your TLS client-certificate fingerprint</li>
<li><strong>SCRAM-SHA-256</strong> — challenge/response, no password on the wire.</li> <li><b>SCRAM-SHA-256</b> &amp; <b>ECDSA-NIST256P-CHALLENGE</b> — challenge/response, no secret on the wire</li>
<li><strong>ECDSA-NIST256P-CHALLENGE</strong> — sign a server challenge with a NIST&nbsp;P-256 key; the private key never leaves your client.</li>
</ul> </ul>
</div> </div>
<div class="block"> <div class="grp">
<h2>Services</h2> <div class="grp-h">services</div>
<ul> <ul class="bul">
<li><strong>NickServ</strong> — registration, grouped nicks, certificates, public keys, vhosts, profile metadata.</li> <li><b>NickServ</b> — registration, grouped nicks, certs, public keys, vhosts, profiles</li>
<li><strong>ChanServ</strong> — founder/access, auto-op, akick, topic and mode locks.</li> <li><b>ChanServ</b> — founder/access, auto-op, akick, topic &amp; mode locks</li>
<li><strong>OperServ / MemoServ / more</strong> — network administration, offline messaging, and games.</li> <li><b>OperServ · MemoServ · more</b> — network admin, offline messages, games</li>
<li><strong>Event-sourced</strong> — every change is an appended event, replayed to rebuild state.</li> <li><b>event-sourced</b> store; SASL relayed to services mechanism-agnostically</li>
<li><strong>SASL over S2S</strong> — the daemon relays mechanisms mechanism-agnostically; the services layer holds the credentials.</li>
</ul> </ul>
</div> </div>
<div class="block"> <div class="grp">
<h2>Security</h2> <div class="grp-h">security</div>
<ul> <ul class="bul">
<li>A native anti-abuse engine in the core — not a bolt-on module.</li> <li>a native anti-abuse engine in the core — connection/nick-flood and mass-join screening</li>
<li>Connection-flood and nick-flood detection, mass-join screening.</li> <li>behavioral and content heuristics with computed-pattern mining</li>
<li>Behavioral and content heuristics with computed-pattern mining.</li> <li>a DEFCON state machine plus DNSBL / MX screening via a native async resolver</li>
<li>A DEFCON state machine and policy, plus DNSBL / MX-blacklist screening via a native async resolver.</li>
</ul> </ul>
</div> </div>
</section>
<section class="connect-strip"> <div class="ln"><span class="t">19:32</span><span class="g">*</span><span class="m i">echoircd nods toward <a href="/connect">#connect</a></span></div>
<div class="cs-in">
<div><h2>Ready?</h2><p>See how to connect and register.</p></div>
<a class="btn primary" href="/connect">Connect &amp; register</a>
</div>
</section>
{% endblock %} {% endblock %}

View file

@ -1,93 +1,28 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}echoIRCd — a modern IRC network in Rust{% endblock %} {% block title %}echoIRCd — a native-Rust IRC network{% endblock %}
{% block content %} {% block topic %}#home — echoIRCd, a native-Rust IRC daemon &amp; services · no fork · no C · no unsafe{% endblock %}
<section class="hero"> {% block chan %}#home{% endblock %}
<div class="badge-row"> {% block cmd %}/connect{% endblock %}
<span class="badge">100% safe Rust</span> {% block buffer %}
<span class="badge">TLS 1.3 · post-quantum</span> <div class="banner">
<span class="badge">Full IRCv3</span> <div class="wordmark">echo<span class="w-hi">IRCd</span><span class="cur cur-lg"></span></div>
<span class="badge">SASL</span> <div class="w-sub">an IRC network written from scratch in Rust — built, not forked</div>
</div> </div>
<h1>A modern IRC network,<br>built from scratch in <span class="accent">Rust</span>.</h1>
<p class="lede">echoIRCd is an original IRC daemon and services suite — no forks, no C, no
<span class="mono">unsafe</span>. Full IRCv3, modern TLS, server-to-server linking, and a
complete services stack with accounts, channels, and SASL.</p>
<div class="cta">
<a class="btn primary" href="/connect">Connect now</a>
<a class="btn" href="/features">Explore features</a>
</div>
<div class="connectline mono">/server irc.echoircd.org +6697</div>
</section>
<section class="split"> <div class="ln"><span class="t">19:28</span><span class="g dim">-!-</span><span class="m dim">Now talking on <b>#home</b></span></div>
<div class="card"> <div class="ln"><span class="t">19:28</span><span class="g dim">-!-</span><span class="m dim">echoIRCd/5.0.0 · irc.echoircd.org · network echoiRCd · <span class="ok">+6697 TLS1.3</span></span></div>
<h3>echoIRCd <span class="tag">the daemon</span></h3> <div class="ln"><span class="t">19:28</span><span class="g n-green">--&gt;</span><span class="m dim">you (~guest@echoircd.org) has joined <b>#home</b></span></div>
<p>A single-threaded core with an epoll reactor pool and a module-per-file architecture. <div class="ln"><span class="t">19:29</span><span class="g n-amber">&lt;echoircd&gt;</span><span class="m">hey — you're looking at an IRC network written from the ground up in Rust.</span></div>
The full channel and user mode set, host cloaking, connection classes, PROXY protocol, <div class="ln"><span class="t">19:29</span><span class="g n-amber">&lt;echoircd&gt;</span><span class="m">original code: no InspIRCd fork, no C, and <span class="kw">#![forbid(unsafe_code)]</span> across the whole tree.</span></div>
ident, and an InspIRCd-style operator privilege system.</p> <div class="ln"><span class="t">19:29</span><span class="g n-amber">&lt;echoircd&gt;</span><span class="m">the daemon speaks full IRCv3 and modern TLS; services handle accounts, channels and SASL.</span></div>
</div> <div class="ln"><span class="t">19:30</span><span class="g">*</span><span class="m i">echoircd slides the tabs your way — <b>#features</b> for the tech, <b>#connect</b> to join</span></div>
<div class="card"> <div class="ln"><span class="t">19:30</span><span class="g n-cyan">&lt;you&gt;</span><span class="m">nice. how do I get on?</span></div>
<h3>echo <span class="tag">the services</span></h3> <div class="ln"><span class="t">19:30</span><span class="g n-amber">&lt;echoircd&gt;</span><span class="m"><a href="/connect">/connect</a> — or open <a href="https://orbit.devtronic.pro">the web client</a> right in your browser.</span></div>
<p>NickServ, ChanServ, OperServ, MemoServ and more, linked over a standard server-to-server
protocol. Account registration, vhosts, grouped nicks, memos and SASL — all backed by an
event-sourced store.</p>
</div>
</section>
<section class="wrap"> <div class="quickfacts">
<h2 class="section-title">What's inside</h2> <a class="qf" href="/features"><span class="qf-k">tls</span> 1.3 · post-quantum</a>
<div class="grid"> <a class="qf" href="/features"><span class="qf-k">ircv3</span> full cap set</a>
<div class="feat"> <a class="qf" href="/features"><span class="qf-k">sasl</span> incl. ecdsa</a>
<div class="ico">&#128274;</div> <a class="qf" href="/features"><span class="qf-k">unsafe</span> 0 lines</a>
<h4>Modern TLS</h4>
<p>Direct TLS on 6697 and WebSocket (wss) on 7799. OpenSSL <em>and</em> rustls backends,
TLS&nbsp;1.3 with post-quantum X25519MLKEM768, and per-host SNI certificates.</p>
</div>
<div class="feat">
<div class="ico">&#9889;</div>
<h4>Full IRCv3</h4>
<p>server-time, message-tags, account-tag, extended-join, batch, labeled-response,
chathistory, multiline, setname, standard-replies, metadata and more.</p>
</div>
<div class="feat">
<div class="ico">&#128273;</div>
<h4>SASL</h4>
<p>PLAIN, EXTERNAL (certificate fingerprint), SCRAM-SHA-256, and
ECDSA-NIST256P-CHALLENGE — sign a challenge with your key, no password on the wire.</p>
</div>
<div class="feat">
<div class="ico">&#129302;</div>
<h4>Services</h4>
<p>Accounts, channels, vhosts, memos and games over a standard S2S link, with SASL relayed
mechanism-agnostically to the services layer.</p>
</div>
<div class="feat">
<div class="ico">&#128737;</div>
<h4>Anti-abuse</h4>
<p>A native security subsystem: connection and nick-flood detection, behavioral and content
heuristics, DEFCON states, and DNSBL / MX screening.</p>
</div>
<div class="feat">
<div class="ico">&#127760;</div>
<h4>Localized</h4>
<p>Server-wide locale catalogs translate numerics and service replies at a single chokepoint.
English stays zero-cost; switch locales on rehash.</p>
</div>
</div> </div>
</section>
<section class="connect-strip">
<div class="cs-in">
<div>
<h2>Jump in.</h2>
<p>Point any IRC client at the network and say hello.</p>
</div>
<table class="mini">
<tr><td>Server</td><td class="mono">irc.echoircd.org</td></tr>
<tr><td>TLS</td><td class="mono">6697</td></tr>
<tr><td>WebSocket</td><td class="mono">7799 (wss)</td></tr>
<tr><td>Network</td><td class="mono">echoiRCd</td></tr>
</table>
</div>
</section>
{% endblock %} {% endblock %}