New Forum
Started by Nick on 2025-11-21 15:54:00
Nick — 2025-11-21 15:54:00
The Rebol Forum was previously hosted on an inexpensive Lunarpages account. Since Lunarpages was purchased by Hostpapa, support for those old hosting packages has been incrementally deprecated. They recently required me to use Cloudflare to manage DNS, and their most recent update disallowed rebol.exe execute permission.
So I've made a quick conversion of the rebolforum app to python, and saved all the historical message posts to a sqlite database, which is downloadable by a link at the bottom of the home page.
The final bb.db and archive.db files used by the original application are still available at:
https://rebolforum.com/bb.db
https://rebolforum.com/archive
The offline reader application from 2010 is still available at:
http://www.rebol.org/view-script.r?script=forum-reader.r
Only a few lines are required:
REBOL [title: "Forum Reader"]
topics: copy [] database: copy []
update: does [
topics: copy []
database: copy load to-file request-file/title/file
"Load Messages:" "" %rebolforum.txt
foreach topic database [
append topics first topic
]
t1/data: copy topics
show t1
]
view layout [
across
t1: text-list 200x400 data topics [
messages: copy ""
foreach [message name time] (at pick database t1/cnt 2) [
append messages rejoin [
message newline newline name " " time newline newline
"---------------------------------------" newline newline
]
]
a1/text: copy messages
show a1
]
a1: area 400x400
btn "Load Locally Saved Messages" [update]
]
Nick — 2025-11-21 15:55:20
Here's code used to migrate the old Rebol data files to sqlite (entirely generated by GPT):
#!/usr/bin/env python
"""
migrate_from_rebol_dbs_fix_encoding.py
Directly parse Rebol bb.db + archive.db into a SQLite forum.sqlite
compatible with the Flask app (topics + posts tables).
Each topic is stored as a Rebol block like:
[
"Topic title"
{Message 1 text}
"Author 1"
"19-Dec-2022/9:55:06-8:00"
{Message 2 text}
"Author 2"
"20-Dec-2022/22:48:42-8:00"
...
]
Strings may be delimited by "..." or {...}.
Key point here: we decode the Rebol files as cp1252 WITH errors="replace"
so that smart quotes/apostrophes survive, and only truly undefined bytes
(e.g., 0x9D) are turned into the replacement character.
"""
import re
import sqlite3
from pathlib import Path
from datetime import datetime
# Paths (adjust if needed)
BB_PATH = Path("bb.db")
ARCHIVE_PATH = Path("archive.db")
OUT_DB = Path("forum.sqlite")
# Rebol data was likely saved in Windows ANSI (cp1252) on Windows
SOURCE_ENCODING = "cp1252"
def iter_top_blocks(src: str):
"""
Yield (start_index, end_index, block_text) for each top-level [...] block,
skipping [ and ] that occur inside "..." or {...}.
"""
blocks = []
i = 0
n = len(src)
while i < n:
c = src[i]
# Skip double-quoted strings with ^ escapes
if c == '"':
i += 1
while i < n:
ch = src[i]
if ch == "^" and i + 1 < n:
i += 2
continue
if ch == '"':
i += 1
break
i += 1
continue
# Skip curly-braced strings (allow nesting and ^ escapes)
if c == "{":
depth = 1
i += 1
while i < n and depth > 0:
ch = src[i]
if ch == "^" and i + 1 < n:
i += 2
continue
if ch == "{":
depth += 1
i += 1
continue
if ch == "}":
depth -= 1
i += 1
continue
i += 1
continue
# Top-level block
if c == "[":
start = i
depth = 1
i += 1
while i < n and depth > 0:
ch = src[i]
# Skip strings inside the block too
if ch == '"':
i += 1
while i < n:
ch2 = src[i]
if ch2 == "^" and i + 1 < n:
i += 2
continue
if ch2 == '"':
i += 1
break
i += 1
continue
if ch == "{":
i += 1
d2 = 1
while i < n and d2 > 0:
ch2 = src[i]
if ch2 == "^" and i + 1 < n:
i += 2
continue
if ch2 == "{":
d2 += 1
i += 1
continue
if ch2 == "}":
d2 -= 1
i += 1
continue
i += 1
continue
if ch == "[":
depth += 1
i += 1
continue
if ch == "]":
depth -= 1
i += 1
if depth == 0:
end = i
blocks.append((start, end, src[start:end]))
break
continue
i += 1
continue
i += 1
return blocks
def tokenize_rebol_block(block: str):
"""
Given a block string like "[ ... ]", return a list of tokens as Python strings.
We treat:
- "..." as a single string token (with ^ escapes)
- {...} as a single string token (with ^ escapes, and nested {})
- everything else as bare tokens split on whitespace (stopping at [ or ]).
"""
assert block[0] == "[" and block[-1] == "]"
src = block[1:-1]
tokens = []
i = 0
n = len(src)
while i < n:
c = src[i]
# Skip whitespace
if c.isspace():
i += 1
continue
# Double-quoted string
if c == '"':
i += 1
buf = []
while i < n:
ch = src[i]
if ch == "^" and i + 1 < n:
buf.append(src[i + 1])
i += 2
continue
if ch == '"':
i += 1
break
buf.append(ch)
i += 1
tokens.append("".join(buf))
continue
# Curly-braced string
if c == "{":
i += 1
buf = []
depth = 1
while i < n and depth > 0:
ch = src[i]
if ch == "^" and i + 1 < n:
buf.append(src[i + 1])
i += 2
continue
if ch == "{":
depth += 1
buf.append(ch)
i += 1
continue
if ch == "}":
depth -= 1
if depth == 0:
i += 1
break
buf.append(ch)
i += 1
continue
buf.append(ch)
i += 1
tokens.append("".join(buf))
continue
# Ignore stray [ or ]
if c in "[]":
i += 1
continue
# Bare token
start = i
while i < n and (not src[i].isspace()) and src[i] not in "[]":
i += 1
tokens.append(src[start:i])
continue
return tokens
# Date parsing for sanity checks
DT_RE = re.compile(
r"(\d{1,2})-([A-Za-z]{3})-(\d{4})/(\d{1,2}):(\d{2})(?::(\d{2}))?"
)
MONTHS = {
"Jan": 1,
"Feb": 2,
"Mar": 3,
"Apr": 4,
"May": 5,
"Jun": 6,
"Jul": 7,
"Aug": 8,
"Sep": 9,
"Oct": 10,
"Nov": 11,
"Dec": 12,
}
def parse_rebol_dt(s: str):
m = DT_RE.search(s)
if not m:
return None
d, mon_s, y, h, mi, se = m.groups()
try:
return datetime(
int(y),
MONTHS.get(mon_s, 1),
int(d),
int(h),
int(mi),
int(se) if se else 0,
)
except Exception:
return None
def load_topics_from_file(path: Path, is_archived_flag: int):
"""
Parse a Rebol db file into a list of topics:
[(is_archived, title, [(msg, author, ts), ...]), ...]
"""
print("Reading", path)
# Decode as cp1252, but replace undefined bytes (like 0x9D) with ?.
raw = path.read_bytes()
text = raw.decode(SOURCE_ENCODING, errors="replace")
blocks = iter_top_blocks(text)
print(" Found", len(blocks), "blocks")
topics = []
for _, _, blk in blocks:
tokens = tokenize_rebol_block(blk)
if len(tokens) < 4:
continue
title = tokens[0].strip()
rest = tokens[1:]
triples = []
# Group rest as (msg, author, ts) triples
for i in range(0, len(rest) - 2, 3):
msg, author, ts = rest[i], rest[i + 1], rest[i + 2]
triples.append((msg, author, ts))
if not triples:
continue
topics.append((is_archived_flag, title, triples))
print(" Parsed", len(topics), "topics from", path.name)
total_posts = sum(len(t[2]) for t in topics)
print(" Total posts from", path.name + ":", total_posts)
return topics
def migrate():
if not BB_PATH.exists():
raise FileNotFoundError(str(BB_PATH) + " not found")
if not ARCHIVE_PATH.exists():
raise FileNotFoundError(str(ARCHIVE_PATH) + " not found")
# Parse archive first, then live
topics = []
topics += load_topics_from_file(ARCHIVE_PATH, is_archived_flag=1)
topics += load_topics_from_file(BB_PATH, is_archived_flag=0)
print()
print("Combined topics:", len(topics))
total_posts = sum(len(t[2]) for t in topics)
print("Combined posts:", total_posts)
# Find latest timestamp for sanity check
latest_dt = None
latest_raw = None
latest_title = None
for is_arch, title, triples in topics:
for msg, author, ts in triples:
dt = parse_rebol_dt(ts)
if dt and (latest_dt is None or dt > latest_dt):
latest_dt = dt
latest_raw = ts
latest_title = title
if latest_dt:
print(
"Latest timestamp in data:",
latest_dt,
"(raw",
repr(latest_raw) + ")",
"in topic",
repr(latest_title),
)
else:
print("WARNING: no timestamps parsed")
# Build SQLite database
if OUT_DB.exists():
print("Removing existing", OUT_DB)
OUT_DB.unlink()
conn = sqlite3.connect(str(OUT_DB))
cur = conn.cursor()
cur.executescript(
"""
CREATE TABLE topics (
id INTEGER PRIMARY KEY,
is_archived INTEGER NOT NULL DEFAULT 0,
title TEXT NOT NULL,
author TEXT,
created_at TEXT,
is_sticky INTEGER NOT NULL DEFAULT 0,
permalink TEXT
);
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
topic_id INTEGER NOT NULL,
body TEXT NOT NULL,
author TEXT,
created_at TEXT,
post_index INTEGER,
FOREIGN KEY (topic_id) REFERENCES topics(id)
);
"""
)
topic_id = 0
for is_arch, title, triples in topics:
topic_id += 1
first_author = triples[0][1]
first_ts = triples[0][2]
cur.execute(
"""
INSERT INTO topics (id, is_archived, title, author, created_at, is_sticky, permalink)
VALUES (?, ?, ?, ?, ?, 0, NULL)
""",
(topic_id, is_arch, title, first_author, first_ts),
)
for idx, (msg, author, ts) in enumerate(triples, start=1):
cur.execute(
"""
INSERT INTO posts (topic_id, body, author, created_at, post_index)
VALUES (?, ?, ?, ?, ?)
""",
(topic_id, msg, author, ts, idx),
)
conn.commit()
c_topics = cur.execute("SELECT COUNT(*) FROM topics").fetchone()[0]
c_posts = cur.execute("SELECT COUNT(*) FROM posts").fetchone()[0]
conn.close()
print()
print("Migration complete. Created", OUT_DB)
print(" topics:", c_topics)
print(" posts: ", c_posts)
if __name__ == "__main__":
migrate()
Nick — 2025-11-21 15:58:16
I may take a few more moments to reconfigure DNS at some point to run this again at rebolforum.com, but for the moment it serves the purpose well enough.
Please let me know if you discover any issue with the application, or if any messages have been omitted.
Sergey_VL — 2025-12-27 12:26:50
Nick, GPT has ruined your once wonderful website design. Now the offline reader application no longer works — "** Command Error: SSL Error: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure". Even after modifying and using my own https2http proxy (http://www.rebol.org/view-script.r?script=reads.r), it still doesn't work because Cloudflare tries to verify whether the request is made by a human and simply blocks program requests :(
Can I suggest a good hosting service that supports Rebol (http://www.pochinoksergey.ru/rebolforum/)? :) Or set up an FTP on my server for (bb.db and the archive) and make it writable for you and readable for everyone via HTTP. Then a small modification to forum-reader.r would allow it to be used further.
Also, a question about personal-programming.com, easiestprogramminglanguage.com, and your other projects — have you considered adding them to the forum archive as folders? They're also disappearing, but might want to preserve them as historical records? Something like https://rebolforum.1y1z.com/topic/815
Sergey_VL — 2025-12-27 12:45:24
Old forum page - 4 kB, new forum page - 356 kB... You programmed better than GPT! :)
Nick — 2025-12-28 05:30:50
Hi Sergey,
Everything is still on the server, but with the rebol executable deprecated by that old host (and since they've required Cloudflare to block questionable traffic) many of those old pieces are not being served. I need time to go though all the old apps that ran on Rebol. I could move them all to any of my VPSs, reconfigure all the DNSs, etc. - just need time to do that (it hasn't been a priority because no one has asked until now). I've got some huge projects to finish during the next few weeks, but will look at the other Rebol projects as time allows!
Nick — 2025-12-28 14:56:15
I am curious what weighed 356k. The entire package for this Python forum, with all code, HTML templates, etc., is an 8k zip file - that's the size of the entire project, which I handle to manage the application on my server, and when interacting with GPT (it's literally instant to upload/download over SCP, to unzip at the command line over SSH, etc.). None of the HTML files are more than 5k uncompressed. Saving the .mhtml file of this page results in a 21k file, but even the plain empty home page of Google.com, which loads instantly in any browser and basically just displays a search bar, results in an 874k .mhtml file. Are you experiencing any slow performance or trouble using the forum application on any platform?
Nick — 2025-12-28 15:54:34
I do appreciate the light weight metric of the Rebol version, but for real life applications, the capability I get with Sqlite and Flask/Python, for example, is absolutely tremendous compared to what was possible with Rebol out of the box. It's like comparing the capabilities of a camper van to roller skates - sure, roller skates are lighter weight and great for rolling around a side walk in front of the house, but not so practical for taking long trips around the country, across highways, up and down mountains, in all weather, etc. And a camper van is trivial in size compared to other modes of transportation, planes, trains, ocean liners, etc. that are currently necessary to make our world function.
Actually, in this comparison, I think Flask/SQLite is more like an Aptera, which is brilliantly functional compared to roller skates - and in this analogy, the Aptera would need to be fully self driving, able to stay completely charged while driving all day in cloudy weather, and be indestructible in crash tests - such a practical trade-off is of course worth having a vehicle heavier than roller skates, for real life needs. I wouldn't want to do all of my daily work and be forced to travel solely on roller skates, no matter how light weight they are ;)
That's how my current tools actually feel, compared to what was possible a few years ago - except it's more like having a fleet of super intelligent solar powered robots driving self-maintaining Apteras which can morph into planes, boats, trucks, campers, and automatically do all the work of running daily errands and completing all the daily tasks... There's not even a remote comparison between that and what it was like having to manually do all the work myself, while running around on roller skates - and my current tools are still far lighter than virtually every other system in production use.
And of course I can instantly switch to Postgres, MSSQL, etc., and instantly integrate with any other common infrastructure/tooling used in any typical established computing environment - kind of like having a camper van that can go off-road, or which can convert to a boat or plane as needed, to be used in environments where roller skates are just not functional (and I need to navigate those sorts of landscapes constantly).
I regularly complete as much work in a day as would have required weeks of development effort in the past. And it's all utterly effortless/instant, as opposed to crushingly difficult and deeply time consuming, to complete those tasks.
Sure, if the only metric is to be as light weight as roller skates, then they win. But as fun as roller skates are, I could never expect them to replace the really capable machines that currently form the foundations of my productivity in 2025.
Nick — 2025-12-28 17:24:06
Please don't get me wrong, I appreciate the craft, skill, deep knowledge and experience that goes into creating beautiful digital works. I still harbor a genuine love for what Rebol accomplished.
Software has become less of an artistic interest, less of an idealistic pursuit, and more of a functional necessity for me, though. In recent years, I've built fewer solutions for my own needs and my own businesses, and more for other, bigger organizations, with more challenging environments to content with, and more complex compliance requirements to satisfy. I just need to get work completed, make software work the way institutions and stakeholders require, satisfy exact detailed specifications, integrate with existing ecosystems, scale effortlessly, and be able to support users with the fewest minutes of my life, and troubles for my mind, soul, and lifestyle.
I thoroughly appreciate that others work to cut solutions down from a few hundred Kb to a few Kb, but the tools I currently use are typically 1/1000th the size of other comparable solutions, and they're instantly available, blindingly fast to use compared to solutions built on other common platforms, and 100% accessible by every single user I ever need to satisfy. They integrate perfectly with every environment I ever come across, and work immediately across every new and old desktop and mobile interface, without any support issues when dealing with normal every day computer users. The tools I rely on satisfy the deepest security and compliance reviews, and my workflows are accepted immediately by IT teams of every flavor. I can install, migrate, manage and update projects in seconds/minutes, rather than hours/days, stand up development and productions environments in minutes, and travel/work from any location/device where a web browser and a command console is available (on virtually any generation of Windows, Mac, Linux, Android, iOS, Chromebook, etc.). My server solutions install instantly on any typical self hosted hardware, VPS, mobile device, etc., and users can access apps instantly on any common device in popular use. I can interoperate with any common API, any typical backend service of any sort, and can run in any typical infrastructure that exists in any common working environment. And most of my server applications are a maximum of tens of KB, packaged as simple zip files. That's teeny tiny by any standard that matters in any environment where I do any work.
And now, all the time, fatigue, frustration, and draining effort it used to take to build, make changes, and support applications from conception to legacy, is largely accomplished effortlessly with the help of AI. I wouldn't trade that for the world, when it comes to accomplishing the sort of work I do.
Nick — 2025-12-28 23:34:36
Sergey,
If there's a reason to support the Rebol offline reader, I could certainly build an endpoint to output the entire database contents in the same historical Rebol data structure. The SQLite database is currently downloadable, if that's of any use.
Nick — 2025-12-29 13:16:26
Sergey,
I need to continue to use Python and SQLite for this forum (just don't have time to retool at the moment). That is heavier than the text data structure used in the old forum app, but it fits the workflow I currently use for all my other production work. Despite being a heavier base architecture, it's far more capable and versatile for the sorts of work I do these days. I'll create an endpoint that outputs the database contents in the historical Rebol plaintext data structure, so the historical reader can still be used.
Sergey_VL — 2026-01-02 13:23:00
Nick,
I’m not talking about using Rebol everywhere :) it’s far from being able to cope with everything in a reasonable time, and for many tasks it’s wiser and faster to achieve the result with other, more suitable tools. It just seems to me that Rebol is like a simple guitar, while Flask/Python/... is a computer with many programs for generating music. On a computer you can quickly lay out notes for different instruments and in a matter of minutes obtain a high-quality musical piece performed by a huge ensemble (drums, strings, winds and other instruments with any effects), but playing a simple guitar, even if not as fast, impeccably and richly in sound, yet soulful and sincere, I suppose... :) Listening to a big symphony orchestra is interesting, but three strings of a balalaika—though they won’t replace it—can inspire no less :) ( https://www.youtube.com/watch?v=ZkPwPd_bsdw )
Nick — 2026-01-05 16:01:41
Sergey,
I like your analogy ;) ... and I was actually showing that balalaika video to my parents last week! There was a point in my music career when I only played good sized venues with bands/ensembles, with lots of supporting teams/staff, trucks full of equipment to set up, etc. I've gotten to the point now where I really prefer to only perform solo, with a looper for backup, maybe sometime with a singer, and with equipment I can carry by myself in one trip from my car - and my creative interests are so different than they were in previous decades. Priorities change...
It's such a shame Rebol didn't gain commercial adoption like Python - it was beautifully conceived, and there was definitely a different satisfaction using it, compared to other tools. At some point I'd like to try working with Rye, because it's built on a well established ecosystem. And perhaps someone will pick up the torch with the old Rebol binaries, using GPT to build it out - perhaps someday I'll look at porting it to web...
Nick — 2026-01-06 16:48:57
Right now though, is an amazing time. To me, today's technical innovations are many orders of magnitude more satisfying than those of any other time. Just as I enjoy playing guitar in a different, simpler way these days, often by choice with just a guitar and looper, because it's simpler and there's a whole wide world to always explore musically, if I was called to perform a tour with all the greatest musicians who ever lived, and all I needed to do was just show up at the gig - all the logistics were magically taken care of - I'd certainly find that more interesting and exciting than playing alone with a looper. I'm sure that experience would make me grow and inspire me more than my own playing alone.
That's the way LLMs and other AI tools feel to me now. Using Python, Flask, and that whole ecosystem actually feels simpler to me than using ever Rebol did, even though it's larger in KB size - because of how deeply supported it is everywhere, and because of the ergonomics of, for example, pip (just one little part of that massively orchestrated ecosystem). Because of pip, there are no installation issues, no issues related to managing versions of libraries - and with the rest of the ecosystem, there are no issues with getting tools accepted, or issues with connecting to and leveraging other massive foreign ecosystems.
As another example, Anvil was a *huge tool in terms of KB size, but the surface area I had to interact with was so dramatically small and ergonomic. Huge projects could be created, deployed, and managed with so much ease - as could tiny 5 minute projects.
Now, with the help of GPT, I'm using tools that are dramatically smaller in KB size - tools which enable the entire development ecosystem I use to fit on a little laptop disconnected from the Internet, if needed (including a usable LLM and all infrastructure, frameworks, libraries, etc.).
But most of the time, I don't even need that - using online tools, I can run everything needed to build, deploy, and manage any number of absolutely massive projects, comfortably from my $40 Android phone, if needed, and at most using netbooks I bought for $80 - and deploying on VPSs which cost $50 per year - or I serve and run created applications just as easily on any 20 year old netbook, $15 phone etc.
And all this is so many orders of magnitude easier than it ever was.
To complete the analogy, it's like right now, I'm actually able to effortlessly go on that massive tour with all the most amazing musicians who ever lived, instead of just sitting and playing with a looper - for me, that's a much more enriching experience. Playing solo with a looper enables a simple world of endless exploration and deep creative satisfaction - not to mention being a practical lightweight solution that enables me to actually continuing gigging under my own power, entirely without relying on anyone else, but I can't help wanting to go on that tour with all the best musicians who ever lived, especially if in the end the logistic and troubles related to doing that incredible journey are even easier than what I can do all on my own.
Reply