site: give echo services its own page + navbar entry; frame it as a separate S2S-linked package, not native to the daemon

This commit is contained in:
Jean Chevronnet 2026-08-30 20:44:08 +00:00
parent 149ab69afe
commit c4836c7dd1
No known key found for this signature in database
GPG key ID: 439666D63A9477E4
7 changed files with 104 additions and 20 deletions

View file

@ -1,15 +1,19 @@
# Introduction
**echoIRCd** is a modern IRC daemon and a full services suite, written from scratch in Rust. It is
original code — not a fork of another ircd — and the entire tree compiles with
`#![forbid(unsafe_code)]`.
**echoIRCd** is a modern IRC daemon, written from scratch in Rust. It is original code — not a fork
of another ircd — and the entire tree compiles with `#![forbid(unsafe_code)]`.
Accounts and channels are handled by **echo services**, a *separate* companion package that links
to the daemon over the server-to-server protocol — the daemon itself is a pure ircd and does not
have services built in.
## What you get
- **echoIRCd** — the server: a single-threaded core with an epoll reactor pool, the full channel
and user mode set, host cloaking, connection classes, and an operator privilege model.
- **echo services** — NickServ, ChanServ, OperServ, MemoServ and more, linked over a standard
server-to-server protocol and backed by an event-sourced store.
- **echoIRCd** — the daemon: a single-threaded core with an epoll reactor pool, the full channel
and user mode set, host cloaking, connection classes, and an operator privilege model. It exposes
the account and SASL *interface* that services plug into.
- **echo services** — a separate package (NickServ, ChanServ, OperServ, MemoServ and more), linked
over the server-to-server protocol and backed by an event-sourced store.
## Highlights

View file

@ -54,6 +54,12 @@ struct ConnectTemplate {
active: &'static str,
}
#[derive(Template)]
#[template(path = "services.html")]
struct ServicesTemplate {
active: &'static str,
}
struct SidePage {
slug: &'static str,
title: &'static str,
@ -95,6 +101,9 @@ async fn features() -> Response {
async fn connect() -> Response {
page(ConnectTemplate { active: "connect" })
}
async fn services() -> Response {
page(ServicesTemplate { active: "services" })
}
async fn docs_index() -> Response {
Redirect::permanent(&format!("/docs/{}", docs::first_slug())).into_response()
@ -177,6 +186,7 @@ async fn main() {
.route("/", get(index))
.route("/features", get(features))
.route("/connect", get(connect))
.route("/services", get(services))
.route("/docs", get(docs_index))
.route("/docs/search.json", get(docs_search))
.route("/docs/:slug", get(docs_page))

View file

@ -127,6 +127,13 @@ code{font-family:var(--mono);font-size:.9em;background:rgba(18,165,148,.09);colo
.foot-links a{color:#aeb8c0;font-size:.9rem}
.foot-links a:hover{color:#fff}
/* services flow diagram */
.flow{display:flex;align-items:center;gap:.55rem;flex-wrap:wrap;margin:1.3rem 0 .3rem}
.flow .node{background:#fff;border:1px solid var(--line2);border-radius:9px;padding:.45rem .85rem;font-weight:500;font-size:.92rem}
.flow .node.accent{border-color:var(--teal);color:var(--teal-dd);background:var(--teal-t)}
.flow .node.accent2{border-color:#c6b6ec;color:#5a3fb0;background:#f3edfd}
.flow .arrow{color:var(--faint);font-family:var(--mono);font-size:.82rem}
@media(max-width:820px){
.hero-in{grid-template-columns:1fr;gap:1.6rem}
.statuscard{max-width:26rem}

View file

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}echoIRCd — a from-scratch IRC server in Rust{% endblock %}</title>
<meta name="description" content="echoIRCd is a modern IRC daemon and services suite, written from scratch in Rust — full IRCv3, modern TLS, SASL.">
<meta name="description" content="echoIRCd is a modern IRC daemon written from scratch in Rust — full IRCv3, modern TLS, SASL. Pairs with echo services, a separate package linked over S2S.">
<meta property="og:title" content="echoIRCd">
<meta property="og:description" content="A from-scratch IRC server, written in Rust.">
<meta property="og:type" content="website">
@ -29,6 +29,7 @@
<nav class="mainnav">
<a href="/"{% if active == "home" %} class="on"{% endif %}>Home</a>
<a href="/features"{% if active == "features" %} class="on"{% endif %}>Features</a>
<a href="/services"{% if active == "services" %} class="on"{% endif %}>Services</a>
<a href="/connect"{% if active == "connect" %} class="on"{% endif %}>Connect</a>
<a href="/docs"{% if active == "docs" %} class="on"{% endif %}>Docs</a>
<a href="https://orbit.devtronic.pro">Web&nbsp;client</a>
@ -51,7 +52,7 @@
</svg>
<span>echoIRCd</span>
</div>
<p class="foot-tag">A native-Rust IRC daemon &amp; services. No forks, no C, no <code>unsafe</code>.</p>
<p class="foot-tag">A native-Rust IRC daemon, with <a href="/services">echo services</a> linked over S2S. No forks, no C, no <code>unsafe</code>.</p>
<nav class="foot-links">
<a href="https://git.devtronic.pro/echo/echoIRCd">echoIRCd</a>
<a href="https://git.devtronic.pro/echo/echo">services</a>

View file

@ -57,13 +57,11 @@
</div>
<div class="doc-block">
<h2>Services</h2>
<ul>
<li><b>NickServ</b> — registration, grouped nicks, certificates, public keys, vhosts, profiles.</li>
<li><b>ChanServ</b> — founder/access, auto-op, akick, topic and mode locks.</li>
<li><b>OperServ · MemoServ · more</b> — network administration, offline messaging, and games.</li>
<li><b>Event-sourced</b> store; SASL relayed to services mechanism-agnostically.</li>
</ul>
<h2>Accounts &amp; services</h2>
<p>echoIRCd itself is only a daemon — it provides the account and SASL <em>interface</em> (the
SASL relay, the account extension, and U-lined service commands). Nickname registration,
channels and the rest are handled by <a href="/services">echo&nbsp;services</a>, a separate
package linked over S2S — not built into the daemon.</p>
</div>
<div class="doc-block">

View file

@ -4,10 +4,11 @@
<section class="hero">
<div class="hero-in">
<div class="hero-main">
<p class="eyebrow">IRC daemon &amp; services · written in Rust</p>
<p class="eyebrow">IRC daemon · written in Rust</p>
<h1>The from-scratch<br>IRC server.</h1>
<p class="hero-sub">echoIRCd is a modern IRC daemon and a full services suite, built from the
ground up in Rust — full IRCv3, modern TLS, and SASL. No forks. No C. No <code>unsafe</code>.</p>
<p class="hero-sub">echoIRCd is a modern IRC daemon, built from the ground up in Rust — full
IRCv3, modern TLS, and SASL. No forks. No C. No <code>unsafe</code>. Pair it with
<a href="/services">echo services</a>, a separate package linked over S2S.</p>
<div class="cta">
<a class="btn btn-primary" href="/connect">Connect to the network</a>
<a class="btn btn-ghost" href="/features">See the features</a>
@ -39,7 +40,7 @@
<div class="fcard"><h3>Full IRCv3</h3><p>server-time, message-tags, account-tag, batch, labeled-response, chathistory, multiline, standard-replies and more.</p></div>
<div class="fcard"><h3>Modern TLS</h3><p>Direct TLS and WebSocket, OpenSSL &amp; rustls backends, TLS&nbsp;1.3 with post-quantum X25519MLKEM768, per-host SNI.</p></div>
<div class="fcard"><h3>SASL, incl. ECDSA</h3><p>PLAIN, EXTERNAL, SCRAM-SHA-256, and ECDSA-NIST256P-CHALLENGE — sign a challenge, no password on the wire.</p></div>
<div class="fcard"><h3>Native services</h3><p>NickServ, ChanServ, OperServ, MemoServ and more over S2S — accounts, channels, vhosts, an event-sourced store.</p></div>
<div class="fcard"><h3>Companion services</h3><p><a href="/services">echo services</a> is a <b>separate</b> package — NickServ, ChanServ and more — that links to the daemon over S2S. It is not built in.</p></div>
<div class="fcard"><h3>Anti-abuse built in</h3><p>A native security engine: connection &amp; nick-flood detection, behavioral/content heuristics, DEFCON, DNSBL/MX screening.</p></div>
</div>
<p class="h-center more"><a href="/features">Everything that's inside &#8594;</a></p>

63
templates/services.html Normal file
View file

@ -0,0 +1,63 @@
{% extends "base.html" %}
{% block title %}Services — echoIRCd{% endblock %}
{% block content %}
<section class="pagehead">
<div class="wrap">
<h1>echo services</h1>
<p>A companion services package that <b>links to echoIRCd over S2S</b>. It is <b>not</b> part of
the daemon — echoIRCd is a pure ircd; the services run as their own program.</p>
</div>
</section>
<section class="wrap doc">
<div class="doc-block">
<h2>Linked, not built in</h2>
<p>echoIRCd is an IRC <em>daemon</em> and nothing more. Accounts, nickname registration and
channel ownership are handled by <b>echo services</b> — a separate Rust program, from its own
repository, that connects to the network as a <b>linked services server</b>.</p>
<p>The split is deliberate: the daemon carries only the account and SASL <em>interface</em> (the
SASL relay, the account extension, and U-lined service commands); the services package holds
the accounts, the logic, and the data.</p>
<div class="flow">
<span class="node">IRC client</span><span class="arrow"></span>
<span class="node accent">echoIRCd</span><span class="arrow">&nbsp;S2S&nbsp;</span>
<span class="node accent2">echo services</span>
</div>
</div>
<div class="doc-block">
<h2>The services</h2>
<div class="cards">
<div class="fcard"><h3>NickServ</h3><p>Register and protect your nick — grouped nicks, certificates, public keys, vhosts, and profiles.</p></div>
<div class="fcard"><h3>ChanServ</h3><p>Channel founders and access lists, auto-op, akick, and topic &amp; mode locks.</p></div>
<div class="fcard"><h3>OperServ</h3><p>Network administration — akills, sessions, and staff tooling.</p></div>
<div class="fcard"><h3>MemoServ</h3><p>Leave messages for accounts while they're offline.</p></div>
</div>
</div>
<div class="doc-block">
<h2>How it fits together</h2>
<ul>
<li>echo services connects to echoIRCd over the <b>server link</b> — the same server-to-server protocol servers use between each other.</li>
<li>The daemon <b>U-lines</b> the services server and names it as the SASL server, so its privileged commands and SASL relay are trusted.</li>
<li>SASL is relayed from the daemon to services <b>mechanism-agnostically</b>, so new mechanisms (like ECDSA) work with no daemon changes.</li>
<li>State is <b>event-sourced</b>: every change is an appended event, replayed to rebuild the database.</li>
</ul>
</div>
<div class="doc-block">
<h2>Run &amp; source</h2>
<p>echo services is its own binary, with its own configuration:</p>
<pre class="code">echo config.toml</pre>
<p>Source: <a href="https://git.devtronic.pro/echo/echo">git.devtronic.pro/echo/echo</a>. See the
docs on <a href="/docs/services">Services</a> and <a href="/docs/linking">Server links</a>.</p>
</div>
</section>
<section class="ctaband">
<div class="wrap ctaband-in">
<div><h2>Register your nick</h2><p class="mono-line">/msg NickServ REGISTER &lt;password&gt; &lt;email&gt;</p></div>
<a class="btn btn-primary" href="/docs/accounts">Accounts &amp; SASL</a>
</div>
</section>
{% endblock %}