projects / nerve-media / after-launch

After launch

What the station's software needed once people were actually using it

2026-10-23 / 6 min read

fig. 0x02nerve radio

Once the website and the station manager were live, and presenters and the committee started using them every day, the gaps showed up. None of this was on the original plan. It's all things people ran into, or asked for once they could see the system working.

Changes show up straight away

The public site caches what it reads from the station manager, which keeps it fast. The downside was that an edit in the manager, like a new show or a changed schedule, didn't show on the site until its cached copy ran out. A cancelled show should disappear from the site now, not later.

So now the manager tells the public site when something changes. Every change it makes works out which areas of the site it touched (the station, shows, the schedule, recordings or events), and changes made close together are gathered into one message. The public site has a small route that takes that message, checks it really came from the manager, and throws away the cached copies of just those areas:

app/api/revalidate/route.ts
const SECRET = process.env.REVALIDATE_SECRET ?? "";function authorised(header: string | null) {  if (SECRET.length < 16 || !header?.startsWith("Bearer ")) return false;  const given = Buffer.from(header.slice("Bearer ".length));  const want = Buffer.from(SECRET);  return given.length === want.length && timingSafeEqual(given, want);}export async function POST(request: Request) {  if (!authorised(request.headers.get("authorization"))) {    return Response.json({ error: "unauthorised" }, { status: 401 });  }  // (reading and checking the list of areas is left out here)  // Expired at once, not served stale while refreshing: the change should  // show on the next page load.  for (const tag of new Set(tags)) revalidateTag(tag, { expire: 0 });  return Response.json({ revalidated: [...new Set(tags)] });}

The secret is shared between the two, and the route compares it in constant timeL7 so nobody can guess it a character at a time from how long the answer takes. It also refuses to work at all with a secret shorter than 16 characters. The other detail is that the areas are expired straight awayL17 rather than refreshed in the background, because the whole point is that the very next page load shows the change.

Are the recordings really there?

Nerve Waves records every show and copies the file to the station's shows file server. Until now, the manager just trusted that it had arrived. If the copy failed, or a file got moved or deleted later, nothing noticed until someone went looking for an old show.

The manager can now read the shows file server itself, even where the server won't list a folder's contents and only answers for files it's asked for by name. A check runs on a timer, set from the admin pages, and looks for every recording's file. Each one ends up found, missing, still waiting or unreadable:

  • Found and missing are what they sound like, and the check also counts which ones changed since last time, so a file that went missing or came back stands out.
  • Waiting is for recordings the recorder hasn't finished yet. The show might still be on air or uploading, so it gets until an hour after its broadcast day before it counts as missing.
  • Unreadable means the file server refused or didn't answer, so those recordings are left as they were rather than all being marked missing because of one bad connection.

The results show on the admin pages, along with the first problem the server gave, so it says why rather than just failing.

With that in place, the public site got the bit listeners actually see. Past recordings on a show's page can now be downloaded.

Running the station

Most of the rest is for the committee.

Listener stats for the licence. The station streams under PPL's webcast licence, which is split into bands by how many streams a station has in a calendar year. There's now a committee page that shows the listener numbers on a chart, which band the station falls in, with a print button. PPL sets the band limits every year, so they live in the settings with this year's figures as the default, instead of being written into the code.

Deleting members, carefully. Admins can now delete a member's account, and it's more involved than it sounds. If that person was the only presenter on any shows, the admin has to choose whether those shows stay (with no presenters) or go with them. Nobody can delete their own account or the last active admin, and two admins can't delete each other at the same moment and leave the station with none. What the member did stays on record without them. API keys they made pass to the admin doing the deleting, and slot requests they had pending are cancelled. Their Active Directory account is left for IT, since the manager can't find it once they're gone.

Studio messages in the playout system. Messages sent to the studio are now an RSS feed that the playout software can read. RSS readers like that one can't send an API key in a header, so the key goes in the feed's address instead.

There were smaller changes too. The long lists (audit logs, API keys and members) now have numbered pages and sorting instead of an endless "load more". Admins can delete shows, there's a details view for each member, images moved from SFTP to storage buckets, a couple of show fields nobody used were taken out, and the docs for importing from the old system and deploying were brought up to date.

Next up is showing unique listeners on the licence page rather than streams per hour, fixing an error the recorder's Windows service can throw when it starts, and finishing the LDAP sign-in from last month now that AD is set up on the station's network.