Skip to content

Commit d362893

Browse files
ten9876claude
andcommitted
Add a public roadmap page
/roadmap sets out what has shipped, what is being built now, and why new radio families are deliberately paused until the headless engine split is finished — the last part being the thing people keep asking about in the issue tracker, and the thing no release note explains. Built on the shared stylesheet: nav, footer, stats and pills are existing components, and the page-specific layout lives in a Roadmap section in styles.css the way every other page keeps its styles. Only roadmap.html takes the ?v=9 cache-bust, since the CSS change is purely additive under new class names and no existing page renders differently. Registered in all three places a new page needs: PAGES in gen-agent-discovery.py, the Stage site step in the deploy workflow, and the generated surface (roadmap.md, sitemap, routes, md-map). The footer's Project column now points Roadmap at the page instead of the GitHub anchor. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 91754f2 commit d362893

8 files changed

Lines changed: 547 additions & 2 deletions

File tree

‎.github/workflows/deploy.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ jobs:
5353
if: ${{ env.CF_TOKEN != '' }}
5454
run: |
5555
mkdir -p _site
56-
cp -R index.html blog.html lineage.html supporters.html 404.html styles.css assets _headers _site/
57-
cp -R robots.txt sitemap.xml index.md blog.md lineage.md supporters.md _site/
56+
cp -R index.html blog.html lineage.html supporters.html roadmap.html 404.html styles.css assets _headers _site/
57+
cp -R robots.txt sitemap.xml index.md blog.md lineage.md supporters.md roadmap.md _site/
5858
cp -R api .well-known functions _routes.json _site/
5959
[ -f _redirects ] && cp _redirects _site/ || true
6060
echo "Staged:" && find _site -maxdepth 3 -type f | sort

‎_routes.json‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
"/lineage",
1111
"/lineage.html",
1212
"/lineage.md",
13+
"/roadmap",
14+
"/roadmap.html",
15+
"/roadmap.md",
1316
"/supporters",
1417
"/supporters.html",
1518
"/supporters.md"

‎functions/md-map.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export const MD_MAP = {
77
'/blog.md': { asset: '/blog.md', tokens: 33127 },
88
'/lineage': { asset: '/lineage.md', tokens: 1994 },
99
'/lineage.md': { asset: '/lineage.md', tokens: 1994 },
10+
'/roadmap': { asset: '/roadmap.md', tokens: 1197 },
11+
'/roadmap.md': { asset: '/roadmap.md', tokens: 1197 },
1012
'/supporters': { asset: '/supporters.md', tokens: 495 },
1113
'/supporters.md': { asset: '/supporters.md', tokens: 495 },
1214
};

‎roadmap.html‎

Lines changed: 308 additions & 0 deletions
Large diffs are not rendered by default.

‎roadmap.md‎

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Roadmap — AetherSDR
2+
3+
> What AetherSDR has shipped, what is being built now, and why new radio families are deliberately paused until the headless engine split is finished.
4+
5+
Source: <https://www.aethersdr.com/roadmap>
6+
7+
Roadmap
8+
9+
## One engine, many front ends
10+
11+
AetherSDR is being split in two: an engine that owns the radio, and a user interface that talks to it over a versioned protocol. Everything below either builds toward that split or waits for it. Phases are an order of work, not dates — we ship when a piece is right rather than when a calendar says so.
12+
13+
### Why the split is worth doing
14+
15+
Today the interface and the radio engine are one program, so the engine cannot run without a screen and the interface cannot be replaced without rebuilding everything. Separating them means the engine can run headless on the machine next to the radio, a second device can drive it across the network, and a browser or a text interface becomes possible without touching radio code. It also means every transmit safeguard lives in one place, below the boundary, where no client can talk its way past it.
16+
17+
6
18+
19+
Radio families behind one interface
20+
21+
3
22+
23+
Native platforms, no emulation
24+
25+
1
26+
27+
Engine, once the split lands
28+
29+
GPL
30+
31+
v3, all of it
32+
33+
#### Shipped
34+
35+
available now
36+
37+
##### The radio boundary v26.7.4
38+
39+
Every radio now sits behind one internal interface, so support for a new model is a self-contained piece of work rather than a change threaded through the whole application. The engine also became a library in its own right, which is what makes a headless build possible at all.
40+
41+
##### Hermes-Lite 2 v26.7.4
42+
43+
Direct-sampling receive and transmit with the signal processing running on your computer, including the noise reduction, filters and transmit chain you already use on a FlexRadio.
44+
45+
##### Networked Icom v26.8.2
46+
47+
Control and audio over the network for Icom transceivers that speak it, with each model's real capabilities read from the radio rather than assumed.
48+
49+
##### ANAN-G2 and RTL-SDR receive
50+
51+
Receive support for Apache Labs ANAN over openHPSDR Protocol 2, and for RTL-SDR dongles. Both demodulate on your computer through the same signal chain.
52+
53+
##### Transmit safeguards, tightened
54+
55+
Every intent that can key a transmitter — including the antenna tuner, which had slipped through — now passes the same refusal checks, and the engine reports a blocked control instead of silently doing nothing.
56+
57+
#### Building now
58+
59+
in progress
60+
61+
##### The control protocol
62+
63+
A versioned conversation between engine and interface: the engine describes what the connected radio can actually do, the interface subscribes to what it needs, and both sides can be upgraded independently.
64+
65+
##### Transmit arbitration and per-client authorisation
66+
67+
Once more than one client can reach the engine, exactly one of them may key the transmitter at a time, and each is authorised separately. This lands together with the protocol rather than after it — a scriptable interface that could reach the transmitter before the guard existed is not something we ship, even briefly.
68+
69+
##### Honest capability reporting
70+
71+
Radios revise what they can do mid-session, and a control that quietly stops matching the radio is worse than one that is plainly unavailable. Every backend is being made to announce its changes so the interface never shows a control the radio cannot honour.
72+
73+
#### Next
74+
75+
after the above
76+
77+
##### Spectrum and audio across the network
78+
79+
A dedicated path for the heavy data, shared memory on the same machine and compressed frames over a network link, so a remote interface gets the same waterfall and audio as one sitting at the radio.
80+
81+
##### A reference thin client
82+
83+
Part of the existing desktop interface, ported to speak the protocol instead of calling the engine directly. It exists to prove the boundary is complete — if a real interface can be built on it, so can any other.
84+
85+
#### Exploring
86+
87+
designed before built
88+
89+
##### A browser interface
90+
91+
Once the protocol carries everything the desktop needs, a web client is an interface problem rather than a radio one. It follows the proven protocol rather than leading it.
92+
93+
##### Two radios in one session
94+
95+
Running a second receiver alongside your main radio — a remote antenna, a second site, a receive-only source — needs decisions about audio routing, settings separation and which radio a transmit request means, before any of it is code.
96+
97+
### Why new radios are paused
98+
99+
We are not adding support for new radio families until the engine split is finished. Each additional radio multiplies work on a boundary that is still moving: capability reporting is not yet consistent across the radios we already support, the original FlexRadio path still bypasses part of the new data route, and the conversion of the command path has not started. Adding a seventh and eighth radio now would mean doing each of those conversions twice more, against code that is changing underneath them.
100+
101+
Finishing first makes every radio after it cheaper to add, safer to review, and less likely to arrive with the quiet gaps that are expensive to find later.
102+
103+
ColibriNANO — parked Yaesu FT-991 — parked Expert Electronics SunSDR — queued ADALM-Pluto — queued
104+
105+
Parked is not declined. Two of these are finished, reviewed contributions waiting on sequencing rather than quality, and the work is credited to the people who wrote it. Radios we already support keep getting fixes and improvements throughout.
106+
107+
### Shaping what comes next
108+
109+
This roadmap is written in public and argued about in public. Feature requests, radio support and design disagreements all live in the issue tracker, and the decisions behind them are written down rather than announced. If something here matters to your station, the discussion is the place to say so.
110+
111+
[Issue tracker](https://github.com/aethersdr/AetherSDR/issues) [Discussions](https://github.com/aethersdr/AetherSDR/discussions) [Sponsor the work](https://opencollective.com/aethersdr)

‎scripts/gen-agent-discovery.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
('index.html', '/', '1.0', 'weekly'),
4141
('blog.html', '/blog', '0.9', 'weekly'),
4242
('lineage.html', '/lineage', '0.5', 'monthly'),
43+
('roadmap.html', '/roadmap', '0.7', 'monthly'),
4344
('supporters.html', '/supporters', '0.6', 'weekly'),
4445
]
4546

‎sitemap.xml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
<changefreq>monthly</changefreq>
1919
<priority>0.5</priority>
2020
</url>
21+
<url>
22+
<loc>https://www.aethersdr.com/roadmap</loc>
23+
<changefreq>monthly</changefreq>
24+
<priority>0.7</priority>
25+
</url>
2126
<url>
2227
<loc>https://www.aethersdr.com/supporters</loc>
2328
<lastmod>2026-09-01</lastmod>

‎styles.css‎

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,3 +996,118 @@ footer.foot { border-top: 1px solid var(--line); margin-top: 30px; padding: 46px
996996
@media (prefers-reduced-motion: reduce) {
997997
.sup-scope { box-shadow: none; }
998998
}
999+
1000+
/* ==========================================================================
1001+
Roadmap — what is shipped, what is being built, and what is deliberately paused
1002+
========================================================================== */
1003+
1004+
.rm-hero .section-head { max-width: 780px; }
1005+
.rm-hero h1 {
1006+
font-size: clamp(32px, 5vw, 52px);
1007+
line-height: 1.06;
1008+
letter-spacing: -0.02em;
1009+
margin: 10px 0 14px;
1010+
}
1011+
.rm-hero .lede { color: var(--ink-soft); font-size: 18px; max-width: 66ch; }
1012+
1013+
/* The through-line callout: one idea the whole roadmap hangs off. */
1014+
.rm-thesis {
1015+
margin: 34px 0 0;
1016+
padding: 26px 28px;
1017+
border-radius: var(--radius);
1018+
border: 1px solid var(--line-hi);
1019+
background: var(--grad-soft);
1020+
display: grid;
1021+
gap: 10px;
1022+
}
1023+
.rm-thesis h2 { margin: 0; font-size: 21px; letter-spacing: -0.01em; }
1024+
.rm-thesis p { margin: 0; color: var(--ink-soft); max-width: 74ch; }
1025+
1026+
/* Status rail — each band is a phase, not a date. */
1027+
.rm-rail { display: grid; gap: 0; margin-top: 12px; }
1028+
.rm-band {
1029+
display: grid;
1030+
grid-template-columns: 168px minmax(0, 1fr);
1031+
gap: 0 28px;
1032+
padding: 30px 0;
1033+
border-top: 1px solid var(--line);
1034+
}
1035+
.rm-band:first-child { border-top: 0; }
1036+
.rm-band-head { position: relative; padding-left: 22px; }
1037+
.rm-band-head::before {
1038+
content: "";
1039+
position: absolute;
1040+
left: 0; top: 7px;
1041+
width: 10px; height: 10px;
1042+
border-radius: 50%;
1043+
background: var(--st, var(--muted));
1044+
box-shadow: 0 0 0 4px rgba(93, 227, 255, 0.10);
1045+
}
1046+
.rm-band-head h3 {
1047+
margin: 0;
1048+
font-size: 15px;
1049+
letter-spacing: 0.02em;
1050+
color: var(--ink);
1051+
}
1052+
.rm-band-head .when {
1053+
display: block;
1054+
margin-top: 5px;
1055+
font-family: var(--mono);
1056+
font-size: 12px;
1057+
color: var(--muted-dim);
1058+
letter-spacing: 0.04em;
1059+
}
1060+
.rm-items { display: grid; gap: 14px; }
1061+
.rm-item {
1062+
border: 1px solid var(--line);
1063+
border-radius: var(--radius-sm);
1064+
background: var(--panel);
1065+
padding: 18px 20px;
1066+
display: grid;
1067+
gap: 7px;
1068+
}
1069+
.rm-item h4 {
1070+
margin: 0;
1071+
font-size: 16px;
1072+
letter-spacing: -0.005em;
1073+
display: flex;
1074+
flex-wrap: wrap;
1075+
align-items: baseline;
1076+
gap: 10px;
1077+
}
1078+
.rm-item h4 .ver {
1079+
font-family: var(--mono);
1080+
font-size: 12px;
1081+
color: var(--cyan);
1082+
border: 1px solid var(--line-hi);
1083+
border-radius: 999px;
1084+
padding: 1px 9px;
1085+
letter-spacing: 0.02em;
1086+
}
1087+
.rm-item p { margin: 0; color: var(--ink-soft); font-size: 14.5px; max-width: 70ch; }
1088+
1089+
/* The pause. Deliberately not styled as a warning — it is a decision. */
1090+
.rm-pause {
1091+
margin-top: 8px;
1092+
border: 1px solid var(--line-hi);
1093+
border-radius: var(--radius);
1094+
background: var(--bg-2);
1095+
padding: 28px 30px;
1096+
display: grid;
1097+
gap: 14px;
1098+
}
1099+
.rm-pause h2 { margin: 0; font-size: 22px; letter-spacing: -0.01em; }
1100+
.rm-pause p { margin: 0; color: var(--ink-soft); max-width: 74ch; }
1101+
.rm-parked { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 2px; }
1102+
.rm-note {
1103+
font-size: 13.5px;
1104+
color: var(--muted);
1105+
border-left: 2px solid var(--line-hi);
1106+
padding-left: 14px;
1107+
max-width: 70ch;
1108+
}
1109+
1110+
@media (max-width: 720px) {
1111+
.rm-band { grid-template-columns: 1fr; gap: 16px; padding: 24px 0; }
1112+
.rm-thesis, .rm-pause { padding: 22px 20px; }
1113+
}

0 commit comments

Comments
 (0)