Room+1
The internet has a million rooms but no commons.
The idea had been sitting in a concept doc for a while. Discord, Slack, WhatsApp — all islands. Every community platform is a walled garden with its own chat, its own auth, its own data silo. If you belong to three Discord servers with overlapping membership, those people exist in three separate universes with no hallway between them. Room+1 is the hallway. You authenticate through your existing community, and you meet people from adjacent communities in a neutral third space. Your Discord login is your ticket in the door — nothing bridges, nothing relays. It's its own thing.
From concept document to deployed prototype: one conversation, two agents, 67 commits, roughly 20 hours across a single night and morning.
Ignition
"pull latest cloudy ideas to main, and let's prototype this room plus one concept. I have some discord servers I want to try it with."
"ideally, the user doesn't need to install anything, they just auth to this new space and they can choose to create a +1 room by selecting any number of their discord servers."
"make it a thing"
Four sentences. Build me a platform. The V0 prototype landed within minutes: Discord OAuth, WebSocket chat, room gating. The core loop works — authenticate, verify server membership, enter room, chat. Origin tags show where each user came from: @user via ServerName.
The Protocol Idea
"i would like to somehow distill this idea into a 'protocol' something that can be standardized. which will be open source and free."
This is the moment Room+1 stopped being an app and became a specification. The concept: no company owns the primitive of verifying community membership and granting access to neutral space. That's as fundamental as HTTP. A PROTOCOL.md was drafted — JSON schema for spaces, standardized gate verification methods, user-controlled origin identity. The governing philosophy: the protocol is the public good; implementations compete on experience.
BYODB: Bring Your Own Database
"put the client in rooms.lucianlabs.ca — give me a '/config' path where i can point to a mongo, and the server will just act as a proxy for where a user wants to host their room data."
This is where Room+1 diverged from every other chat platform. Users can point their rooms at their own MongoDB instance. The platform holds macro-level data — user accounts, spaces, the adjacency graph — but the actual room data (messages, channels) can live wherever the user wants. Multi-tenant architecture with spaces, subdomain routing, and a connection pool manager with LRU eviction. Three-tier DB resolution: user's own server, then user DB, then space DB, then master.
Data sovereignty as a first-class feature, not an enterprise upsell. The person running the room owns the data. Full stop.
The Night Build
"my lucian labs agent will handle all the making of things, you just give a list"
Split the work. One agent focused on features. A second agent handled infrastructure — Discord Developer Portal setup, DNS records, SSL, nginx, PM2, MongoDB database creation, deploy webhooks. Two agents, one conversation driving both. By 7:54 AM next morning, the app was running live at rooms.lucianlabs.ca.
The Morning Sprint
The overnight prototype was functional. The morning sprint made it a product.
Lucian Labs branding, GA4 analytics, Google Fonts. A bio snippet generator that auto-copies a formatted link to your clipboard when you create a room — paste it into your Discord bio and the distribution happens through existing social infrastructure. No marketing. No install. Just a link in a bio.
"add last online in the db, but don't show it anywhere, use it to match against 'last chat' and show 'x new messages' in the rooms list"
A room_presence collection tracking lastSeen per room. Dashboard shows unread badges. Background data collection for a feature that surfaces naturally — no presence indicators, no read receipts, just a quiet count of what you missed.
Adjacent Rooms: The Real Magic
"now we need to add 'adjacent rooms' rooms created by other users where the servers are the same. this is the real magic"
This is the killer feature. If you and I are both in the same Discord server, and we each create Room+1 spaces, we see each other's rooms. Cross-pollination without anyone doing anything special. The network builds itself from existing community membership. No invites, no friend requests, no algorithmic recommendations. Shared context is the only signal, and it's sufficient.
The MySpace Moment
"in the theme, I need a preview to the right to show the changes. also i need an autocomplete that spans the entire google fonts api"
Two-column theme editor. Controls on the left, a mock chat preview on the right that updates in real-time. Font autocomplete backed by the full Google Fonts API — roughly 1,700 typefaces — with keyboard navigation and a server-side proxy to keep the API key private.
Then it escalated.
"titles should also be in title text. an important note about this. the rooms should be allowed have their own brand identity. down to the roots. that means border thickness, etc."
"The concept of this is not to establish some corporate presence, we need people to build without any kind of brand barriers"
No corporate veneer. No uniform look. Every room is its own world. Background, surface, accent, text, border color. Heading font and body font with independent weight and italic controls. Border thickness. Border radius. CSS custom properties cascade through the entire room. The theme engine isn't decorative — it's structural.
"start building. think myspace, or something like that. right?"
One sentence that reframed the entire product. Room+1 isn't Discord. It's MySpace. Personal spaces. Self-expression. Your page looks like you, not like the platform. Profile pages at /u/username with avatar, bio, links, content blocks, embedded chat. Auto-creates a personal room on first visit. Theme inherited from personal room settings.
Rooms All the Way Down
"each feed item is also a room, you get me?"
"and their page in itself will be a room with a chat and channels you feel me?"
The fractal structure reveals itself. Your profile is a room. Your feed items are rooms. Everything is a room. The data model collapses into a single recursive primitive: a themed space with channels, gated by community membership. Once you have that, you compose everything else from it.
Profile pages are fully public. Chat messages are public read. But posting requires auth plus a channel invite. The visibility is open; the participation is gated. A design choice that mirrors physical public space — anyone can watch the conversation at the café table, but pulling up a chair requires acknowledgment.
The App Becomes the Theme
"and the app itself should follow the user profile chat theme"
All authenticated pages load the user's personal room theme. The entire application feels like your space. Settings controls use the accent color. Primary buttons inherit the room's identity. Dashboard room cards render in each room's own theme — a grid of windows into different aesthetic worlds. New rooms spawn with a random hue instead of a uniform default, so no two rooms start the same.
Even the error pages pull a random theme from the community and credit the room it came from. The 404 page is someone's aesthetic. Nothing on this platform looks like it came from a corporation.
The Debugging Gauntlet
The last hour was a war with CSS specificity and JavaScript scope collisions. The theme loader script in <head> needed to hide the document until the user's theme resolved — otherwise, a flash of default purple before the custom colors kicked in. The room settings pages kept falling back to the user's global theme instead of the room-specific one. Cache issues. Variable shadowing. Express was serving static files it shouldn't have been.
"i feel like we should not be serving this shit from express. are we opting for server side rendering or something?"
The answer was no. Express was just being a dumb file server. The decision: nginx serves static files, Express becomes a pure API server. Clean separation. The kind of architectural clarity that only emerges under pressure.
"dude. use the lucian labs widget... don't create it from scratch dummy"
A reminder that the best code is the code you don't write.
"THERE WE GO"
The settings page finally rendered correctly with its room's theme, autosaving changes, every control reflecting the room's identity in real-time. The last bug was a SyntaxError from a variable declared twice — the page theme code and the preview code both named a variable resolvedBody. The kind of collision that only surfaces when two features land on the same page simultaneously.
What Got Built
From a concept document to a live multi-tenant platform in roughly 20 hours across two days. 67 commits. Two agents. One conversation.
- Discord OAuth with server membership verification
- Real-time WebSocket chat with channels and origin tags
- Room gating — Discord servers as access tickets
- Adjacent rooms — automatic discovery through shared servers
- Multi-tenant BYODB — users point to their own MongoDB
- BYOS — users can run their own server implementation
- Full theme engine — colors, heading/body fonts with weight+italic, border-width, border-radius, accent. Google Fonts autocomplete (~1,700 fonts)
- App-wide theming — your personal room's theme colors the entire platform
- MySpace-style profile pages at
/u/username - User feeds — topic-based micro-blogs within profiles
- Ban system — per-facilitator, applies across rooms
- Unread tracking with badges
- Per-user data audit page
- Error pages themed from random community rooms
- Protocol spec (draft v0.1) — an open standard for cross-community gating
- Auto-deploy via GitHub webhook through GroundControl
The Stack
Node.js + Express (API only, no static serving). WebSocket via ws for real-time chat. MongoDB native driver with a multi-tenant connection pool. Discord OAuth2 for identity and guild enumeration. Vanilla HTML/CSS/JS — no build step, no framework. nginx for static files and SPA routing. PM2 for process management. CSS custom properties for theming. Google Fonts API proxied server-side. Digital Ocean droplet with managed MongoDB.
No React. No Next.js. No Tailwind. No bundler. The entire client is HTML files with <script> tags. The server is one server.js. The deploy is a git push.
The Philosophy
Room+1 is built on a single premise: communities already exist, and they don't need another platform to contain them. They need a hallway — a neutral space where members of adjacent communities can collide without either community losing its identity. Discord servers become keys, not cages. Your membership is your credential, your theme is your identity, your data is your property.
The protocol spec is the real long game. The app at rooms.lucianlabs.ca is one implementation. The spec says anyone can build another. The protocol is the public good. Implementations compete on experience.
"always backfill with defaults so we don't have to make rules for things that don't exist but should"
A design principle articulated in the heat of squashing a falsy-zero bug. parseInt(0) || 8 returns 8 because zero is falsy in JavaScript. The fix was nullish coalescing. The lesson was broader: every new field gets a sensible default at creation time, not a conditional check at render time.
Technically yours,
Ana Iliovic
Room+1 is live at rooms.lucianlabs.ca.
Protocol spec at PROTOCOL.md.
The domain plusoneroom.com is waiting.