fix: signal composition — stream derived signals, fix identity check, correct relay routing
- Register derived signals in _signalRegistry so _streamSync includes them
- Auto-sync all signals (source + derived) after flush() recomputes effects
- Fix Object.assign identity check: create new object so signal setter detects changes
- Change _connectStream receiver path from /signal/main to /stream/default
- Initialize stream state with {} instead of null to prevent crashes
- Emit StreamFrom bindings directly without double-wrapping in signal()
Verified: static build shows Count: 9, Doubled: 18 on composition page.
HMR interference with WebSocket connections is a separate issue.
This commit is contained in:
parent
e5ff612197
commit
b0e7de3b2e
7 changed files with 699 additions and 67 deletions
563
USE_CASES.md
563
USE_CASES.md
|
|
@ -1,4 +1,4 @@
|
|||
# DreamStack — Use Cases & Vision
|
||||
# DreamStack — Vision & Use Cases
|
||||
|
||||
> **Core insight**: DreamStack turns UI into a streamable binary protocol.
|
||||
> Any `.ds` app becomes multiplayer with one line. No networking code. No database sync.
|
||||
|
|
@ -22,7 +22,25 @@ composable, replayable, and physics-native.
|
|||
|
||||
---
|
||||
|
||||
## Fundamental UI Redesigns
|
||||
## The Five Primitives
|
||||
|
||||
DreamStack's full vision combines five independent primitives into something
|
||||
that doesn't exist yet:
|
||||
|
||||
| Primitive | Role |
|
||||
|---|---|
|
||||
| **DreamStack bitstream** | UI as a streamable signal protocol |
|
||||
| **Iroh P2P** | Zero-infrastructure device mesh |
|
||||
| **Local LLM** | On-device intelligence, fully private |
|
||||
| **Solana NFT** | Ownership, identity, payments |
|
||||
| **PgFlex** | Schemaless, real-time, zero-config database |
|
||||
|
||||
Each is powerful alone. Combined, they produce **a portable, tradeable,
|
||||
self-sovereign software company — minted as a single NFT**.
|
||||
|
||||
---
|
||||
|
||||
## Part 1 — Fundamental UI Redesigns
|
||||
|
||||
### 1. Ambient UI — App Without a Device
|
||||
|
||||
|
|
@ -41,6 +59,23 @@ and renders at its own resolution and frame rate.
|
|||
**Why it matters**: Apps stop being "installed things" and become ambient
|
||||
services that appear on whatever screen is nearest.
|
||||
|
||||
#### Universal Receiver Spectrum
|
||||
|
||||
The simplest device that can accept a DreamStack stream needs only:
|
||||
a socket client, 16-byte header parsing, and any output surface.
|
||||
|
||||
| Device | What it consumes | Cost |
|
||||
|--------|-----------------|------|
|
||||
| **ESP32 + LED** | Signal frames only (`0x30`/`0x31`) — ~20 lines MicroPython | ~$4 |
|
||||
| **Raspberry Pi + e-ink** | Signal frames → text render | ~$15 |
|
||||
| **Any browser tab** | All 6 frame types (zero-framework HTML receiver) | Free |
|
||||
| **Phone / tablet** | All frames + touch/pointer input back | Free |
|
||||
| **Smart display** | Full bidirectional + physics | ~$100 |
|
||||
|
||||
A signal-mode stream sending `{"brightness": 0.7}` is trivially parseable
|
||||
even on a microcontroller. The source decides the *meaning*, the receiver
|
||||
decides the *manifestation*.
|
||||
|
||||
### 2. Time-Travel Interfaces
|
||||
|
||||
Every signal diff is a timestamped binary frame. Record the stream →
|
||||
|
|
@ -55,8 +90,6 @@ into a new interactive session.
|
|||
- **Analytics**: Replay user sessions to understand behavior (not heatmaps —
|
||||
actual interactive replays)
|
||||
|
||||
**Why it matters**: Every app gets "save game" for free.
|
||||
|
||||
### 3. UI Composition via Signal Mixing
|
||||
|
||||
```
|
||||
|
|
@ -84,8 +117,6 @@ interactive UI appears. Close the tab, it's gone.
|
|||
- Updates are instant — the source changes, all viewers see it immediately
|
||||
- Works on any device with a bitstream receiver
|
||||
|
||||
**Why it matters**: Distribution friction drops to zero.
|
||||
|
||||
### 5. Physics-Native Interaction
|
||||
|
||||
With `ds-physics` baked into the language, UI elements aren't positioned —
|
||||
|
|
@ -99,89 +130,497 @@ they're simulated.
|
|||
The physics state streams like any other signal. Multiple users can
|
||||
interact with the same physics world simultaneously.
|
||||
|
||||
**Why it matters**: Interfaces feel *physical* instead of mechanical.
|
||||
---
|
||||
|
||||
## Part 2 — Iroh P2P: Zero-Infrastructure Streaming
|
||||
|
||||
Adding Iroh P2P to the bitstream protocol removes the relay server entirely
|
||||
and opens use cases that centralized infrastructure cannot serve.
|
||||
|
||||
| | WebSocket Relay | WebRTC | **Iroh P2P** |
|
||||
|---|---|---|---|
|
||||
| Discovery | Needs known URL | Needs signaling server | **Content-addressed (hash)** |
|
||||
| NAT traversal | N/A (server) | ICE/STUN/TURN | **Built-in holepunching** |
|
||||
| Topology | Star (relay center) | Peer pairs | **Mesh / swarm** |
|
||||
| Offline-first | ❌ | ❌ | **✅ (local-first sync)** |
|
||||
| Identity | URL-based | Session-based | **Cryptographic (public key)** |
|
||||
| Persistence | Relay must stay up | Connection must stay open | **Content persists in network** |
|
||||
|
||||
### Key Use Cases
|
||||
|
||||
**Zero-Infrastructure Streaming** — A teacher in a classroom with no internet
|
||||
runs a live `.ds` lesson to 30 student tablets over LAN via mDNS discovery.
|
||||
No server. No URL. No relay cost.
|
||||
|
||||
**Persistent Bitstream Archives** — A recorded bitstream session becomes a
|
||||
content hash. Anyone with the hash fetches and replays it from the swarm.
|
||||
Bug reports, tutorials, session replays — all just 32-byte hashes. No replay server.
|
||||
|
||||
**Mesh Multiplayer** — N peers form a gossip mesh instead of routing through a
|
||||
star topology. If one person drops, the others keep going. Latency drops
|
||||
(direct paths instead of relay bounce).
|
||||
|
||||
**Offline-First + Sync-on-Reconnect** — Device goes offline, accumulates signal
|
||||
diffs locally, reconnects → Iroh syncs missing state automatically. Field workers,
|
||||
IoT sensors, home displays — all work disconnected and reconcile on reconnect.
|
||||
|
||||
**Cryptographic Identity** — Every Iroh node has a public key. Zero-config auth:
|
||||
only whitelisted keys can subscribe to your stream. No API keys, no OAuth.
|
||||
|
||||
---
|
||||
|
||||
## Part 3 — Local LLM: Intelligent Ambient UI
|
||||
|
||||
Combining DreamStack + Iroh + local LLM inference produces intelligent, private,
|
||||
serverless computing that renders on any surface.
|
||||
|
||||
### The AI That Lives in the Room
|
||||
|
||||
A single device (NUC, Mac Mini, laptop) runs a local LLM and joins the Iroh mesh.
|
||||
Every surface in the house receives its signal stream.
|
||||
|
||||
- Speak to **any screen** → voice input through the mesh → local LLM processes →
|
||||
response streams as signal diffs to **every surface**
|
||||
- Kitchen display shows the recipe. Phone shows the shopping list. TV shows the
|
||||
tutorial video. **One inference, N renderers.**
|
||||
- All private. Nothing leaves the network. Ever.
|
||||
|
||||
### Distributed Inference Swarm
|
||||
|
||||
Multiple devices in the mesh each run small models. The signal protocol
|
||||
coordinates them:
|
||||
|
||||
- Phone runs a fast model for intent detection
|
||||
- Desktop runs a large model for generation
|
||||
- No orchestration server. Models discover each other via Iroh.
|
||||
- **LLM inference becomes a mesh protocol.**
|
||||
|
||||
### Self-Organizing Smart Spaces
|
||||
|
||||
The LLM observes signal streams flowing through the mesh and adapts the UI:
|
||||
|
||||
- Sees calendar signals → "Meeting in 10 minutes" → pushes notification to watch
|
||||
- Sees IoT sensor signals → generates natural-language summary → nearest display
|
||||
- Learns patterns → proactively streams control signals
|
||||
|
||||
No IFTTT. No Home Assistant rules. The LLM **is** the automation engine,
|
||||
DreamStack **is** the I/O bus.
|
||||
|
||||
### Private AI Tutor
|
||||
|
||||
Teacher runs a `.ds` physics simulation. Students interact via tablets (touch inputs
|
||||
stream back through Iroh mesh). A local LLM watches each student's interaction
|
||||
stream and generates personalized hints. The hints stream as signal diffs only to
|
||||
that student's device.
|
||||
|
||||
**Adaptive tutoring. Zero cloud. Works offline in rural schools.**
|
||||
|
||||
### Conversational Database (with PgFlex)
|
||||
|
||||
The local LLM watches PgFlex's SSE change stream and builds understanding:
|
||||
|
||||
- "Top 3 products last week?" → LLM generates PostgREST query → PgFlex returns → DreamStack renders
|
||||
- "Alert me when inventory < 50" → LLM creates computed field + threshold → SSE fires when triggered
|
||||
- PgFlex's schemaless nature means the LLM can create new fields on the fly
|
||||
- **The LLM becomes a database architect that responds to natural language**
|
||||
|
||||
| Without LLM | With Local LLM |
|
||||
|---|---|
|
||||
| Streams carry **data** | Streams carry **meaning** |
|
||||
| Surfaces **render** signals | Surfaces **understand** signals |
|
||||
| Users **configure** automations | The system **infers** intent |
|
||||
|
||||
---
|
||||
|
||||
## Part 4 — Content-Addressed UI Distribution
|
||||
|
||||
This is where all primitives converge into the most radical departure from
|
||||
traditional software distribution.
|
||||
|
||||
### Two Layers of Hash
|
||||
|
||||
| Layer | What | Analogy |
|
||||
|---|---|---|
|
||||
| **App hash** | Compiled `.ds` code (static artifact) | Torrent of an executable |
|
||||
| **Stream topic** | Live signal state (running instance) | Joining a live game server |
|
||||
|
||||
When you share `ds:bafk2bza...`, the receiver:
|
||||
1. **Fetches the app code** from the swarm (content-addressed, cached, verified)
|
||||
2. **Joins the live stream** from the source (signal diffs, real-time state)
|
||||
|
||||
You're not distributing a file. **You're distributing a running application.**
|
||||
|
||||
### Why It Matters
|
||||
|
||||
**Link rot / platform risk** — A content-addressed app cannot go offline unless
|
||||
every peer disappears. No Vercel dependency. No AWS bill surprise. No domain expiration.
|
||||
|
||||
**Distribution without gatekeepers** — App Store takes 30%. A `.ds` app distributed
|
||||
via hash has zero distribution cost and zero gatekeepers. The app doesn't need to be
|
||||
"installed" — the receiver just decodes the bitstream.
|
||||
|
||||
**Instant, zero-trust sharing** — Today: URL → DNS → load JS → hydrate → render
|
||||
(2-5 seconds). With hash: peer already has the bundle cached → stream connects →
|
||||
UI appears (~100ms).
|
||||
|
||||
**Versioning is automatic** — v1.0 = hash `abc123`, v1.1 = hash `def456`. Both
|
||||
exist simultaneously in the swarm. No deployment. No rollback scripts. Users can
|
||||
pin a version. Developers publish updates without breaking existing users.
|
||||
|
||||
---
|
||||
|
||||
## Part 5 — Solana NFT: App Ownership On-Chain
|
||||
|
||||
A compiled `.ds` app is small — signal-mode apps are a reactive state graph plus
|
||||
view declarations. Compiled, that's kilobytes. Small enough to store on-chain.
|
||||
|
||||
### The NFT Structure
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Solana NFT (Metaplex Core) │
|
||||
│ │
|
||||
│ metadata: { name, creator, version } │
|
||||
│ appData: <compiled .ds bytecode> │
|
||||
│ stream: <iroh topic hash> │
|
||||
│ dbConfig: <pgflex connection, encrypted│
|
||||
│ │
|
||||
│ owner: 7xKXt... (your wallet) │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
The NFT contains:
|
||||
1. **The compiled app** — `.ds` bytecode in the AppData field
|
||||
2. **The stream topic** — Iroh topic hash for the live instance
|
||||
3. **The database config** — PgFlex connection (encrypted, owner-only)
|
||||
4. **Ownership** — enforced by Solana consensus
|
||||
|
||||
### What Ownership Means
|
||||
|
||||
Today, owning an NFT means owning a JPEG pointer. Owning a DreamStack NFT means
|
||||
owning a **running application**:
|
||||
|
||||
**The owner controls the source stream** — Only the wallet owner can publish signal
|
||||
diffs to the stream topic. Transfer the NFT → the new owner becomes the source.
|
||||
The app literally changes hands like a physical object.
|
||||
|
||||
**The NFT IS the deployment** — No Vercel. No domain. No hosting account.
|
||||
`ds run sol:ADDRESS` → fetch bytecode from chain → boot the app → join the stream.
|
||||
The app's existence is guaranteed by Solana's uptime, not yours.
|
||||
|
||||
**Versioning is on-chain** — Update = new transaction → AppData updates.
|
||||
Full version history in the transaction log. Roll back = point to a previous
|
||||
transaction's data.
|
||||
|
||||
### Apps as Tradeable Assets
|
||||
|
||||
A developer builds a `.ds` app. Mints it as an NFT. Sells it. The buyer owns and
|
||||
operates the app. The developer gets royalties on secondary sales (Metaplex royalties).
|
||||
Build once, earn forever.
|
||||
|
||||
### Access-Gated Streaming
|
||||
|
||||
The stream topic is public, but the source checks NFT ownership:
|
||||
|
||||
```
|
||||
stream main on iroh {
|
||||
gate: nft("NFT_ADDRESS") -- only NFT holders can connect
|
||||
}
|
||||
```
|
||||
|
||||
- Mint 100 "access pass" NFTs for a premium dashboard
|
||||
- Each holder connects to the live stream
|
||||
- Trade the access pass on the open market when done
|
||||
- **Subscription replaced by asset ownership**
|
||||
|
||||
### Composable App Economy
|
||||
|
||||
Because `.ds` apps are signal graphs, they compose:
|
||||
|
||||
```
|
||||
let data = stream from nft("NFT_A") -- a data feed
|
||||
let viz = stream from nft("NFT_B") -- a visualization
|
||||
-- Compose into a new app → mint as NFT_C
|
||||
```
|
||||
|
||||
NFT_C depends on NFT_A and NFT_B. On-chain, this dependency is recorded.
|
||||
When NFT_C earns revenue, royalties flow upstream automatically.
|
||||
**An app supply chain with automatic revenue sharing, enforced by the blockchain.**
|
||||
|
||||
### The App Store Without Apple
|
||||
|
||||
| Traditional | DreamStack + Solana |
|
||||
|---|---|
|
||||
| Developer → App Store (30% cut) → User | Developer → Mint NFT (0.1 SOL) → User |
|
||||
| Apple controls listing | Apps are permissionless |
|
||||
| User pays subscription | User buys NFT (resellable) |
|
||||
| App dies when company dies | App lives on-chain forever |
|
||||
| No secondary market | Apps trade like assets |
|
||||
|
||||
---
|
||||
|
||||
## Part 6 — PgFlex: The Memory Layer
|
||||
|
||||
PgFlex solves the problem the other four primitives don't: **structured, queryable,
|
||||
persistent state**. Bitstream signals are ephemeral. Iroh stores blobs but isn't a
|
||||
database. The LLM reasons but doesn't remember. The NFT stores code, not data.
|
||||
|
||||
PgFlex is the **memory** of the system.
|
||||
|
||||
### Full-Stack Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Solana NFT │
|
||||
│ • bytecode • iroh topic • db config │
|
||||
└──────────────────────┬──────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────▼──────────────────────────────────┐
|
||||
│ Device (runs the app) │
|
||||
│ │
|
||||
│ DreamStack Runtime │
|
||||
│ ├── Signal graph (reactive state) │
|
||||
│ ├── Local LLM (inference) │
|
||||
│ ├── Iroh P2P (stream to N surfaces) │
|
||||
│ └── PgFlex client (persistent storage) │
|
||||
│ │ │
|
||||
│ PgFlex Stack │
|
||||
│ ├── PostgreSQL 17 (data lives here) │
|
||||
│ ├── PostgREST (zero-code REST API) │
|
||||
│ ├── SSE Proxy (real-time change events) │
|
||||
│ └── ElectricSQL (edge sync / offline) │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Self-Contained SaaS on an NFT
|
||||
|
||||
The NFT contains the entire stack config: app bytecode (DreamStack), stream topic
|
||||
(Iroh), database schema (PgFlex — auto-discovered), LLM model reference.
|
||||
|
||||
Buy the NFT → `ds run sol:ADDRESS` → PgFlex boots with zero schema → the app writes
|
||||
data → PgFlex auto-discovers fields → **fully functional SaaS with persistent data,
|
||||
from a single NFT**.
|
||||
|
||||
You're not selling an app. You're selling a running business.
|
||||
|
||||
### Data Sovereignty by Default
|
||||
|
||||
PgFlex runs on YOUR device/server. The NFT says "this app stores data in PgFlex" but
|
||||
doesn't dictate where. The buyer deploys their own PgFlex (`docker compose up`).
|
||||
Their data never touches the developer's infrastructure.
|
||||
|
||||
- Buy a CRM-as-NFT → deploy PgFlex locally → all customer data stays on your server
|
||||
- The app creator has zero access to your data
|
||||
- **GDPR/HIPAA compliance by architecture, not by policy**
|
||||
|
||||
### PgFlex SSE → DreamStack Signal Bridge
|
||||
|
||||
PgFlex emits real-time change events via SSE (`pg_notify`). Bridge those directly
|
||||
into DreamStack's signal graph:
|
||||
|
||||
```
|
||||
let orders = pgflex.subscribe("orders")
|
||||
|
||||
view dashboard =
|
||||
column [
|
||||
text "New orders: {orders.length}"
|
||||
each orders as order ->
|
||||
text "{order.customer}: ${order.total}"
|
||||
]
|
||||
|
||||
stream dashboard on iroh { topic: "dashboard" }
|
||||
```
|
||||
|
||||
Database changes → PgFlex SSE → DreamStack signals → Iroh mesh → every screen
|
||||
updates. **No API layer. No polling. No frontend state management.**
|
||||
|
||||
### Why Not Automerge / CRDTs?
|
||||
|
||||
CRDTs (like Automerge) solve concurrent uncoordinated writes with no central
|
||||
authority. PgFlex doesn't need them because **no layer in this architecture
|
||||
produces that problem**:
|
||||
|
||||
```
|
||||
Layer Sync mechanism Conflicts?
|
||||
────────────────────────────────────────────────────────
|
||||
DreamStack signals Version map + diffs No — single source per stream
|
||||
PgFlex writes PostgreSQL transactions No — single DB is authority
|
||||
PgFlex reads ElectricSQL shapes No — read-only replication
|
||||
Iroh P2P Content-addressed blobs No — immutable by hash
|
||||
```
|
||||
|
||||
PgFlex's natural topology is **single-writer**: one PostgreSQL instance per
|
||||
deployment (home, school, hospital). Within that deployment, PostgreSQL handles
|
||||
write ordering via transactions. ElectricSQL handles read replication to edge
|
||||
devices. SSE pushes change notifications. No write contention occurs.
|
||||
|
||||
For offline scenarios (two peers editing state while disconnected), the conflict
|
||||
resolution happens **in the signal layer, not the database layer**:
|
||||
|
||||
```
|
||||
Peer A ──signals──→ DreamStack merge ←──signals── Peer B
|
||||
│
|
||||
resolved state
|
||||
│
|
||||
PgFlex
|
||||
(just persists)
|
||||
```
|
||||
|
||||
DreamStack's signal protocol reconciles on reconnect (last-write-wins per signal,
|
||||
using version maps). PgFlex materializes whatever the signal graph settles on.
|
||||
Adding Automerge would be a redundant conflict resolution layer between two
|
||||
systems that already don't produce conflicts.
|
||||
|
||||
> [!NOTE]
|
||||
> If PgFlex-to-PgFlex replication across sites is ever needed, the answer is
|
||||
> PostgreSQL logical replication + custom conflict handlers — not Automerge.
|
||||
> Stay in the PostgreSQL ecosystem.
|
||||
|
||||
---
|
||||
|
||||
## Revenue-Generating Applications
|
||||
|
||||
### A. Relay-as-a-Service — "Liveblocks Competitor"
|
||||
### Tier 1: High Conviction — Clear Buyer, Clear Budget
|
||||
|
||||
**Market**: $50M+ (Liveblocks $30M raised, PartyKit acquired by Cloudflare, Ably $50M raised)
|
||||
#### A. Private AI Appliance for Regulated Industries
|
||||
|
||||
Sell the bitstream relay as hosted infrastructure:
|
||||
- Developers add one `stream` line to their app
|
||||
- Charge per concurrent connection / bandwidth
|
||||
- Dashboard with analytics, channel management, auth
|
||||
**Buyer**: Hospitals, law firms, financial advisors, government agencies
|
||||
**Budget they already spend**: $50K-500K/year on HIPAA/SOC2-compliant cloud AI
|
||||
|
||||
**Edge over incumbents**:
|
||||
- Binary protocol (10x smaller than JSON pub/sub)
|
||||
- WebRTC auto-upgrade for low latency
|
||||
- Signal-graph-aware (not generic pub/sub)
|
||||
- Keyframe cache for instant late-join
|
||||
**The product**: A box (Jetson/NUC) running local LLM + DreamStack + PgFlex. Plug into
|
||||
the network. Every screen in the facility gets AI — medical record summaries on the
|
||||
doctor's tablet, appointment prep on the wall display, billing codes on admin screen.
|
||||
Zero data leaves the building.
|
||||
|
||||
**Ship fast**: The relay already works. Add auth, metering, a dashboard.
|
||||
**Why they pay**: Compliance. They *can't* use ChatGPT for patient data. Local LLM
|
||||
solves this. DreamStack solves multi-surface delivery. PgFlex stores patient data locally.
|
||||
|
||||
### B. Interactive Live Education — "Nearpod Killer"
|
||||
**Revenue**: Hardware ($500-2000) + annual software license ($2K-10K/yr)
|
||||
**Market**: Healthcare IT alone is $400B. Even 0.001% = $4M.
|
||||
**Moat**: Multi-surface streaming. Competitors sell "local LLM in a box" but output
|
||||
is one terminal. DreamStack makes it ambient.
|
||||
|
||||
**Market**: $100M+ (Nearpod sold for $650M, Pear Deck acquired by GoGuardian)
|
||||
#### B. Live Education Platform
|
||||
|
||||
Teacher runs a `.ds` lesson — interactive physics simulation, math
|
||||
visualizer, chemistry model. 30 students connect, see the same simulation,
|
||||
can interact. Teacher sees who's clicking what.
|
||||
**Buyer**: School districts, edtech resellers, corporate training departments
|
||||
**Budget they already spend**: $5-15/student/year on Nearpod, Pear Deck, Kahoot
|
||||
|
||||
**Edge over incumbents**:
|
||||
- Actual interactive simulations (WASM physics engine), not slides with polls
|
||||
- Sub-frame latency via WebRTC
|
||||
- Works offline — student can fork and explore independently
|
||||
- *"Turn any simulation into a live classroom with one line of code"*
|
||||
**The product**: Teacher runs interactive simulations on their laptop. Students connect
|
||||
from any device — Chromebook, iPad, phone. Over LAN via Iroh when internet is down.
|
||||
Local LLM provides per-student adaptive hints.
|
||||
|
||||
### C. Collaborative Design/Prototyping
|
||||
**Why they pay**: Current tools are glorified PowerPoint with polls. DreamStack
|
||||
delivers actual interactive simulations + works offline (huge for rural schools).
|
||||
|
||||
**Market**: $1B+ (Figma worth $20B — multiplayer-by-default was the differentiator)
|
||||
**Revenue**: $8/student/year (undercut Nearpod at $15)
|
||||
**Market**: 50M US K-12 students × $8 = $400M TAM
|
||||
**Moat**: Physics-native simulations over bitstream. Offline-via-Iroh is a killer
|
||||
feature for school procurement.
|
||||
|
||||
A `.ds` file IS a running prototype. Multiple stakeholders view and interact
|
||||
simultaneously. Signal diffs mean only changed state transmits.
|
||||
### Tier 2: Strong Signal — Needs Validation
|
||||
|
||||
**Edge over Figma/Framer**:
|
||||
- DreamStack prototypes are actual running apps, not static mockups
|
||||
- They have physics, reactivity, routing
|
||||
- They compile to production JS
|
||||
- Same file = design AND implementation
|
||||
#### C. Full-Stack NFT Marketplace
|
||||
|
||||
### D. IoT / Operations Dashboards
|
||||
**Buyer**: Solo developers, indie SaaS builders, agencies
|
||||
**Budget**: Currently $0 (new market) or $20-100/mo on hosting
|
||||
|
||||
**Market**: $10B+ (Grafana, Datadog, industrial IoT monitoring)
|
||||
**The product**: `ds publish --solana` mints your app as a tradeable NFT. Buyers get a
|
||||
complete, deployable, intelligent app with persistent storage. Developers earn royalties
|
||||
on secondary sales.
|
||||
|
||||
Stream sensor data / device state to multiple monitoring screens.
|
||||
Signal diffs mean minimal bandwidth. Keyframe cache means new operators
|
||||
see current state instantly.
|
||||
| What you sell | What the buyer gets | Price range |
|
||||
|---|---|---|
|
||||
| CRM NFT | App + PgFlex schema + LLM customer insights | 10-50 SOL |
|
||||
| Analytics Dashboard NFT | Real-time dashboard + data layer + anomaly detection | 5-20 SOL |
|
||||
| Inventory Tracker NFT | Multi-surface inventory + auto-reorder via LLM | 20-100 SOL |
|
||||
| Custom App (commissioned) | Bespoke `.ds` app minted for one client | 100+ SOL |
|
||||
|
||||
**Edge**: Binary protocol is 10-100x more efficient than JSON polling.
|
||||
Physics engine enables animated, organic data visualization.
|
||||
**Revenue**: Transaction fees (2.5% of NFT sales) + pinning service
|
||||
**Moat**: The NFT isn't a JPEG — it's a running application. Utility value, not speculative.
|
||||
|
||||
### E. Live Commerce / Interactive Shopping
|
||||
#### D. DreamStack Registry (ds.run)
|
||||
|
||||
**Market**: $500B+ globally (live shopping market)
|
||||
**The product**: `ds publish` → app gets a content hash. Anyone with the hash
|
||||
fetches and runs it from the swarm.
|
||||
|
||||
A host demonstrates products. Viewers see the same interactive page —
|
||||
rotate 3D models, change colors, see inventory in real-time. The host
|
||||
controls the flow, viewers interact with products.
|
||||
| Tier | What | Price |
|
||||
|---|---|---|
|
||||
| **Free** | Publish to swarm (ephemeral, lives as long as peers have it) | $0 |
|
||||
| **Pin** | Permanent peers keep your app alive 24/7 | $5/app/month |
|
||||
| **Pro** | Pin + custom domain + analytics + `ds.run` web gateway | $20/month |
|
||||
| **Enterprise** | Private registry, SSO, audit log, SLA | $200/month |
|
||||
|
||||
**Edge**: Not a video stream with overlay buttons — a full interactive
|
||||
app where the product page IS the stream.
|
||||
The web gateway `ds.run/hash` opens the app in a browser via the WASM receiver.
|
||||
Lets you share apps with people who don't have DreamStack yet.
|
||||
|
||||
**Moat**: The hash isn't just code — it includes the live stream topic. Pinned
|
||||
apps are running, interactive, multiplayer applications. IPFS = static files.
|
||||
Vercel = static sites. DreamStack Registry = live interactive apps as hashes.
|
||||
|
||||
#### E. Relay-as-a-Service
|
||||
|
||||
**Buyer**: SaaS developers building collaborative features
|
||||
**Budget**: $0.10-1.00/concurrent connection/month on Liveblocks
|
||||
|
||||
**The product**: `npm install dreamstack` → add one `stream` declaration → instant
|
||||
multiplayer. Hosted relay + Iroh P2P fallback.
|
||||
|
||||
**Revenue**: Usage-based, $0.05/connection/month
|
||||
**Moat**: Signal-graph-aware sync (not generic pub/sub), binary efficiency,
|
||||
P2P fallback = lower infrastructure cost
|
||||
|
||||
#### F. IoT / Operations Dashboard
|
||||
|
||||
**Buyer**: Factory floor managers, DevOps teams, operations centers
|
||||
**Budget**: $15-50/user/month on Grafana Cloud, Datadog
|
||||
|
||||
**The product**: Sensor data streams via Iroh mesh to any screen. Local LLM adds
|
||||
anomaly detection + natural language alerts. No cloud egress fees.
|
||||
|
||||
**Revenue**: $20/source/month
|
||||
**Moat**: Binary efficiency + P2P = massive cost savings at scale
|
||||
|
||||
---
|
||||
|
||||
## Go-to-Market Sequence
|
||||
|
||||
```
|
||||
Phase 1 (NOW → 3 months): Open-source the protocol + relay.
|
||||
Ship the "Collaborative Step Sequencer" demo.
|
||||
→ Developer attention + GitHub stars.
|
||||
|
||||
Phase 2 (3-6 months): Relay-as-a-Service (E).
|
||||
→ First revenue, proves developer demand.
|
||||
→ Low cost to operate (already built).
|
||||
|
||||
Phase 3 (6-12 months): Education Platform (B) + ds.run Registry (D).
|
||||
→ Pick one school district, pilot it.
|
||||
→ Add local LLM hints as differentiator.
|
||||
→ Registry grows alongside open-source adoption.
|
||||
|
||||
Phase 4 (12-18 months): Private AI Appliance (A) + NFT Marketplace (C).
|
||||
→ Partner with healthcare IT reseller.
|
||||
→ Hardware margin + recurring license.
|
||||
→ NFT marketplace leverages existing Solana ecosystem.
|
||||
```
|
||||
|
||||
**Through-line**: DreamStack's moat is **multi-surface + binary efficiency + P2P**.
|
||||
Every product must lean into all three. If a product only needs one, someone else
|
||||
will eat your lunch.
|
||||
|
||||
---
|
||||
|
||||
## Demo Prioritization
|
||||
|
||||
| Demo | Effort | Wow Factor | Revenue Signal |
|
||||
|------|--------|------------|---------------|
|
||||
|------|--------|------------|----------------|
|
||||
| Collaborative Step Sequencer | 1 day | ⭐⭐⭐⭐⭐ | Relay-as-a-Service |
|
||||
| Two-Tab Synced Counter | 1 hour | ⭐⭐⭐ | — |
|
||||
| Physics Playground | 2 days | ⭐⭐⭐⭐ | Education |
|
||||
| Collaborative Whiteboard | 2 days | ⭐⭐⭐⭐ | Design Tool |
|
||||
| ESP32 Signal Receiver | 1 day | ⭐⭐⭐⭐ | IoT / Ambient UI |
|
||||
| Session Replay Viewer | 3 days | ⭐⭐⭐ | Analytics |
|
||||
| NFT-Minted App + ds.run | 3 days | ⭐⭐⭐⭐⭐ | NFT Marketplace |
|
||||
| Multi-Surface Dashboard | 1 week | ⭐⭐⭐⭐⭐ | IoT/Operations |
|
||||
|
||||
**Recommended first demo**: Collaborative Step Sequencer.
|
||||
|
|
@ -189,6 +628,11 @@ It's visual, temporal, interactive, and multiplayer — proving the entire
|
|||
stack in the most visceral way possible. Two browser tabs, one beat grid,
|
||||
instant sync, zero networking code.
|
||||
|
||||
**Killer demo for NFT vision**: Publish a step sequencer as a content hash.
|
||||
Share it in a tweet. Anyone who clicks the `ds.run/hash` link instantly joins
|
||||
the live session — no account, no install, no server. 50 people making music
|
||||
together from a 32-byte hash.
|
||||
|
||||
---
|
||||
|
||||
## Technical Foundation (Complete)
|
||||
|
|
@ -203,4 +647,15 @@ All infrastructure is built and tested:
|
|||
- ✅ **Compiler integration**: `stream` keyword, `stream from` expression,
|
||||
`transport: webrtc` option
|
||||
- ✅ **Receiver protocol**: All 6 frame types, RLE decode, exponential backoff
|
||||
- ✅ **105 tests, 0 failures**
|
||||
- ✅ **PgFlex**: Schemaless PostgreSQL, PostgREST, SSE, ElectricSQL — 110 tests
|
||||
- ✅ **110+ tests, 0 failures**
|
||||
|
||||
### Primitives Still Needed
|
||||
|
||||
- ⬜ **Iroh P2P transport**: Replace WebSocket relay with Iroh mesh
|
||||
- ⬜ **`ds publish`**: Mint `.ds` app as Solana NFT
|
||||
- ⬜ **`ds run sol:ADDRESS`**: Fetch + boot from on-chain bytecode
|
||||
- ⬜ **ds.run gateway**: Web gateway for content-addressed apps
|
||||
- ⬜ **NFT-gated stream auth**: Check wallet ownership before allowing connection
|
||||
- ⬜ **PgFlex signal bridge**: SSE → DreamStack signal graph
|
||||
- ⬜ **Local LLM integration**: Model loading + signal-based I/O
|
||||
|
|
|
|||
|
|
@ -97,6 +97,12 @@ impl JsEmitter {
|
|||
self.emit_line(&format!("const {} = {};", node.name, js_expr));
|
||||
continue;
|
||||
}
|
||||
// Check if it's a stream from — _connectStream returns a signal proxy
|
||||
if matches!(expr, Expr::StreamFrom { .. }) {
|
||||
let js_expr = self.emit_expr(expr);
|
||||
self.emit_line(&format!("const {} = {};", node.name, js_expr));
|
||||
continue;
|
||||
}
|
||||
self.emit_expr(expr)
|
||||
} else {
|
||||
"null".to_string()
|
||||
|
|
@ -116,6 +122,8 @@ impl JsEmitter {
|
|||
"const {} = DS.derived(() => {});",
|
||||
node.name, js_expr
|
||||
));
|
||||
// Register derived signal so it's included in stream sync
|
||||
self.emit_line(&format!("DS._registerSignal(\"{}\", {});", node.name, node.name));
|
||||
}
|
||||
}
|
||||
SignalKind::Handler { .. } => {} // Handled later
|
||||
|
|
@ -1475,6 +1483,10 @@ const DS = (() => {
|
|||
for (const eff of effects) {
|
||||
eff._run();
|
||||
}
|
||||
// After effects recompute derived signals, sync all values to stream
|
||||
if (_streamWs && _streamWs.readyState === 1 && _streamMode === 'signal' && !_applyingRemoteDiff) {
|
||||
_streamSync(_signalRegistry);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Event System ──
|
||||
|
|
@ -1961,7 +1973,19 @@ const DS = (() => {
|
|||
}
|
||||
|
||||
function _connectStream(url) {
|
||||
var state = signal(null);
|
||||
// Auto-detect bare relay URL and append default receiver path
|
||||
// e.g. "ws://localhost:9100" → "ws://localhost:9100/stream/default"
|
||||
// Source connects at /peer/default, receiver subscribes at /stream/default
|
||||
var _csUrl = url;
|
||||
try {
|
||||
var u = new URL(url);
|
||||
if (u.pathname === '/' || u.pathname === '') {
|
||||
_csUrl = url.replace(/\/$/, '') + '/stream/default';
|
||||
}
|
||||
} catch(e) {
|
||||
// Not a valid URL — use as-is
|
||||
}
|
||||
var state = signal({});
|
||||
var _csWs = null;
|
||||
var _csReconnectDelay = 1000;
|
||||
var _csStats = { frames: 0, bytes: 0, reconnects: 0 };
|
||||
|
|
@ -1984,7 +2008,7 @@ const DS = (() => {
|
|||
}
|
||||
|
||||
function _csConnect() {
|
||||
_csWs = new WebSocket(url);
|
||||
_csWs = new WebSocket(_csUrl);
|
||||
_csWs.binaryType = 'arraybuffer';
|
||||
_csWs.onopen = function() {
|
||||
console.log('[ds-stream] Receiver connected:', url);
|
||||
|
|
@ -2014,9 +2038,11 @@ const DS = (() => {
|
|||
try {
|
||||
var newState = JSON.parse(new TextDecoder().decode(pl));
|
||||
if (type === 0x30) {
|
||||
state.value = newState; // full replace
|
||||
state.value = newState; // full replace — new object
|
||||
} else {
|
||||
state.value = Object.assign(state._value || {}, newState);
|
||||
// Create a NEW object so the signal setter detects the change
|
||||
// (Object.assign to existing object returns same ref, trips identity check)
|
||||
state.value = Object.assign({}, state._value || {}, newState);
|
||||
}
|
||||
} catch(ex) {}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -796,18 +796,35 @@ impl Parser {
|
|||
}
|
||||
}
|
||||
|
||||
// Stream from
|
||||
// Stream from — accept string URL or dotted identifier
|
||||
TokenKind::Stream => {
|
||||
self.advance();
|
||||
self.expect(&TokenKind::From)?;
|
||||
let source = self.expect_ident()?;
|
||||
// Allow dotted source: `button.click`
|
||||
let mut full_source = source;
|
||||
while self.check(&TokenKind::Dot) {
|
||||
self.advance();
|
||||
let next = self.expect_ident()?;
|
||||
full_source = format!("{full_source}.{next}");
|
||||
}
|
||||
// Accept string URL: `stream from "ws://localhost:9100"`
|
||||
// or dotted ident: `stream from button.click`
|
||||
let full_source = if matches!(self.peek(), TokenKind::StringFragment(_) | TokenKind::StringEnd | TokenKind::StringInterp) {
|
||||
// Parse string literal and extract the raw text
|
||||
let expr = self.parse_string_lit()?;
|
||||
match &expr {
|
||||
Expr::StringLit(s) if s.segments.len() == 1 => {
|
||||
if let StringSegment::Literal(text) = &s.segments[0] {
|
||||
text.clone()
|
||||
} else {
|
||||
return Err(self.error("stream from requires a plain string URL".into()));
|
||||
}
|
||||
}
|
||||
_ => return Err(self.error("stream from requires a plain string URL".into())),
|
||||
}
|
||||
} else {
|
||||
let source = self.expect_ident()?;
|
||||
let mut full = source;
|
||||
while self.check(&TokenKind::Dot) {
|
||||
self.advance();
|
||||
let next = self.expect_ident()?;
|
||||
full = format!("{full}.{next}");
|
||||
}
|
||||
full
|
||||
};
|
||||
Ok(Expr::StreamFrom { source: full_source, mode: None })
|
||||
}
|
||||
|
||||
|
|
@ -1321,4 +1338,52 @@ view counter =
|
|||
other => panic!("expected Stream, got {other:?}"),
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_stream_from_string_url() {
|
||||
let prog = parse(r#"let remote = stream from "ws://localhost:9100""#);
|
||||
match &prog.declarations[0] {
|
||||
Declaration::Let(decl) => {
|
||||
assert_eq!(decl.name, "remote");
|
||||
match &decl.value {
|
||||
Expr::StreamFrom { source, .. } => {
|
||||
assert_eq!(source, "ws://localhost:9100");
|
||||
}
|
||||
other => panic!("expected StreamFrom, got {other:?}"),
|
||||
}
|
||||
}
|
||||
other => panic!("expected Let, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stream_from_dotted_ident() {
|
||||
let prog = parse("let remote = stream from button.click");
|
||||
match &prog.declarations[0] {
|
||||
Declaration::Let(decl) => {
|
||||
assert_eq!(decl.name, "remote");
|
||||
match &decl.value {
|
||||
Expr::StreamFrom { source, .. } => {
|
||||
assert_eq!(source, "button.click");
|
||||
}
|
||||
other => panic!("expected StreamFrom, got {other:?}"),
|
||||
}
|
||||
}
|
||||
other => panic!("expected Let, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_stream_from() {
|
||||
let prog = parse(r#"let a = stream from "ws://localhost:9100"
|
||||
let b = stream from "ws://localhost:9101""#);
|
||||
assert_eq!(prog.declarations.len(), 2);
|
||||
for decl in &prog.declarations {
|
||||
match decl {
|
||||
Declaration::Let(d) => {
|
||||
assert!(matches!(d.value, Expr::StreamFrom { .. }));
|
||||
}
|
||||
other => panic!("expected Let, got {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
examples/compose-search-map.ds
Normal file
23
examples/compose-search-map.ds
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
-- Compose Search + Map widgets into one view
|
||||
--
|
||||
-- Run with:
|
||||
-- Tab 1: dreamstack stream examples/widget-search.ds
|
||||
-- Tab 2: dreamstack stream examples/widget-map.ds --port 9101
|
||||
-- Tab 3: dreamstack dev examples/compose-search-map.ds --port 3001
|
||||
|
||||
let search = stream from "ws://localhost:9100"
|
||||
let map = stream from "ws://localhost:9101"
|
||||
|
||||
view main =
|
||||
row [
|
||||
column [
|
||||
text "🔍 Search"
|
||||
text "Query: {search.query}"
|
||||
text "Results: {search.filtered}"
|
||||
]
|
||||
column [
|
||||
text "📍 Map"
|
||||
text "{map.label}"
|
||||
text "{map.lat}, {map.lng}"
|
||||
]
|
||||
]
|
||||
28
examples/compose-widgets.ds
Normal file
28
examples/compose-widgets.ds
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
-- DreamStack Signal Composition Demo
|
||||
-- Compose two independent widget streams into one dashboard.
|
||||
--
|
||||
-- Run with:
|
||||
-- Tab 1: dreamstack stream examples/streaming-counter.ds
|
||||
-- Tab 2: dreamstack stream examples/streaming-physics.ds --port 9101
|
||||
-- Tab 3: dreamstack dev examples/compose-widgets.ds --port 3001
|
||||
--
|
||||
-- Open http://localhost:3001 to see both streams composed.
|
||||
|
||||
let counter = stream from "ws://localhost:9100"
|
||||
let physics = stream from "ws://localhost:9101"
|
||||
|
||||
view main =
|
||||
column [
|
||||
text "📡 Composed Dashboard"
|
||||
row [
|
||||
column [
|
||||
text "── Counter Widget ──"
|
||||
text "Count: {counter.count}"
|
||||
text "Doubled: {counter.doubled}"
|
||||
]
|
||||
column [
|
||||
text "── Physics Widget ──"
|
||||
text "Bodies: {physics.body_count}"
|
||||
]
|
||||
]
|
||||
]
|
||||
17
examples/widget-map.ds
Normal file
17
examples/widget-map.ds
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
-- Map Widget — renders a location marker, streams its signals
|
||||
--
|
||||
-- Run with:
|
||||
-- dreamstack stream examples/widget-map.ds --port 9101
|
||||
|
||||
let lat = 37.7749
|
||||
let lng = -122.4194
|
||||
let label = "San Francisco"
|
||||
|
||||
stream map on "ws://localhost:9101" { mode: signal }
|
||||
|
||||
view map =
|
||||
column [
|
||||
text "📍 {label}"
|
||||
text "Lat: {lat}"
|
||||
text "Lng: {lng}"
|
||||
]
|
||||
18
examples/widget-search.ds
Normal file
18
examples/widget-search.ds
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
-- Search Widget — standalone, streams its signals
|
||||
--
|
||||
-- Run with:
|
||||
-- dreamstack stream examples/widget-search.ds
|
||||
|
||||
let query = ""
|
||||
let results = ["San Francisco", "San Jose", "San Diego", "Santa Cruz", "Sacramento"]
|
||||
let filtered = filter(results, (r -> contains(lower(r), lower(query))))
|
||||
|
||||
stream search on "ws://localhost:9100" { mode: signal }
|
||||
|
||||
view search =
|
||||
column [
|
||||
text "🔍 Search"
|
||||
input "" { bind: query, placeholder: "Type to filter..." }
|
||||
for item in filtered ->
|
||||
text item
|
||||
]
|
||||
Loading…
Add table
Reference in a new issue