Phosphor Progress — From Parser to Federation
July 31, 2026
Twenty-nine commits. Six phases. One language. Here’s where Phosphor stands after the most intense development sprint yet.
The Journey So Far
When I wrote about building the Phosphor parser, the language was a lexer and a dream. Phase 1 was five tasks — tokenizer, parser, declarative processor, hot-reload, BBS wiring. It felt like a lot at the time.
It wasn’t.
Phases 1-2: Foundation
The declarative layer came first. Define your BBS structure — menus, boards, channels, file areas, themes — in a readable syntax. Save the file. The BBS syncs to the database automatically. No restart, no recompile.
Then the imperative layer: an expression evaluator with variables, control flow, and functions. Event handlers that respond to login, chat, board posts. Scheduled events that fire on daily, hourly, or cron schedules. And the reactive binding system — bind declarations that auto-update screens when data changes.
on login {
if user.first_login {
show "welcome.ans"
mail from "SysOp" to user {
subject = "Welcome to Phosphor!"
body = "Welcome aboard, ${user.display_name}!"
}
}
}
Phase 3: Screens and Widgets
Phosphor needed its own screen system. Not just Java screens — Phosphor-defined screens with lifecycle, state, navigation, and built-in widgets.
The imperative layer gave us PhosphorScreen with lifecycle hooks (on enter, on exit, on input, on tick). The declarative layer gave us layout blocks — row, column, text, input, table, box — composable widgets with IDs for targeted updates.
screen "ChatDashboard" {
state messages: List = []
layout {
column {
text "Live Chat" style: "bold"
table messages {
columns: [user, message, time]
}
input "Type a message..." on_submit {
chat_send(session, input)
}
}
}
on update {
messages = chat_recent(50)
}
}
Phase 4: Door Games
A BBS without door games is just a chat server with extra steps. Phosphor 4.1 built the door game engine — a framework for Phosphor-defined games that run inside the BBS terminal. 4.2 shipped a complete dungeon crawler game with scoreboard, proving the engine could handle real game logic.
Phase 5: The Editor
If you’re going to write Phosphor code, you need a Phosphor editor. Phase 5 built one — inside the BBS itself. Syntax highlighting, auto-complete, snippets, live parse error reporting, and save-to-apply hot-reload. You edit a .phos file through the terminal, save, and watch the BBS reconfigure itself in real-time.
Phase 6: Java Integration
Phosphor needed to talk to Java. Phase 6 added three annotations:
@PhosphorFunction— expose Java methods as Phosphor builtins@PhosphorBinding— bind Java state to Phosphor reactive variables@PhosphorScreenType/@PhosphorWidget— register custom screen types and widgets
This is what makes Phosphor practical. The BBS’s existing Java services — auth, mail, chat, boards — are all available as Phosphor functions. You don’t rewrite the BBS in Phosphor. You extend it.
Phase 7: The Web Layer
This is where it got serious.
7a: Testing. Phosphor can now test itself. test blocks with mocks, assertions, and integration tests. JUnit integration so Phosphor tests run in mvn test alongside Java tests. JaCoCo coverage reports. The language tests its own compiler.
7b: REST API + WebSocket. The web layer. Phosphor api blocks become Javalin REST routes. websocket blocks become live WebSocket endpoints with bidirectional events — emit, broadcast, emit_to, fire. One event bus, three surfaces (terminal, WebSocket, REST). A change from any surface propagates to all of them.
websocket "/ws/dashboard" {
require auth
bind online = session_count()
bind channels = chat_channels()
on connect {
emit("dashboard", {online: online, channels: channels})
}
on message {
match msg.type {
"join_channel" => {
chat_join(session, msg.channel)
emit("joined", {channel: msg.channel})
}
}
}
}
7c: Federation. The big one. Multi-node sync for BBS federation.
syncable declarations define what gets replicated. Two modes: event (append-only, immutable) and entity (updatable by natural key). Conflict resolution strategies — last_write_wins, first_write_wins, highest_value, merge, even custom functions. The sync runtime handles envelopes, deduplication, backfill on reconnect, tombstones for deletes, and retention purging. All transported over the existing jnet mesh.
syncable "trade_signal" {
mode = "event"
fields = {
symbol: String,
action: String,
shares: Int,
price: Float,
user: String,
timestamp: Time
}
conflict_resolution = "last_write_wins"
retention = 7d
write = ["vip"]
}
Boards and channels marked federated = true sync across nodes automatically. A user on the Bronxville node sees messages from the Ohio node in real-time. The sysop controls which resources stay local and which get shared.
The Numbers
- 29 commits across 7 phases
- ~7,000 tests passing (Java + Phosphor, same JUnit suite)
- 15 files in the WebSocket task alone — parser, AST, evaluator, runtime, registry, server wiring
- The language spec document is 6,465 lines
What’s Next
The pipeline continues with preferences (7d), user scripting sandbox (7e), and the final feature wave (7f): ANSI art pipeline, module marketplace, achievements, AI integration, calendar, themes, and macros.
The BBS is back. And it’s yours.