Every website starts with an idea. Follow the complete journey—from the first spark to a live website
people can visit.
Step 1 of 6
1. It starts with someone.
Maybe it’s Lara. Maybe it’s her upcoming 17th birthday. Every website begins with a reason—something
you want to create, share, or remember. Selection gives that idea a place of its own.
Lara's Idea ➔ Selection Studio ➔ Birthday Website ➔ Pages ➔ 🚀 Live
2. One idea can become a whole website.
Lara’s invitation doesn’t have to be a single static page. It can become a complete website with
everything her guests need—the invitation, party location, photo wall, and RSVP details.
Invitation
/home
Party Location
/venue
Photo Wall
/gallery
RSVP
/rsvp
3. Now you make it yours.
Add the title, change the details, choose the colors, and shape every page in Selection Studio.
You’re working in a private draft, so nothing becomes public until you’re ready.
Your changes are saved
4. When it feels right, publish.
One click turns your private draft into a live website. Selection checks everything behind the
scenes and prepares it for visitors.
[✓] Draft loaded from D1
[✓] Tenant + Experience + Page ownership verified
[✓] Website structure validated
[✓] Public version prepared in R2
5. Now people can visit.
The editing tools stay behind. Your guests see only the finished website—clean, fast, and ready to
open on any device.
https://selection.rs/lara/invitation
Lara's 17th Birthday Bash 🎉
6. Want to see what happens behind the scenes?
The Selection Architecture Atlas explains how identity, security,
pages, publishing, and runtime work together to turn one simple
idea into a live website.
Kanonska mapa sistema — Experience V2
Selection Atlas arhitekture
Tehnički i procesni manifest — od prve posete do Live Website-a
0. Kako čitati ovaj dokument
Ovaj dokument prati jednu jedinu priču: čovek prvi put vidi Selection, podnosi zahtev, biva
odobren,
dobija svoj digitalni prostor, ulazi u Studio, bira Experience, uređuje Page, čuva Draft,
klikne
Publish
i na kraju otvara pravi javni Website. Svaka skripta i svaki folder objašnjeni su u trenutku
kada se
prirodno pojavljuju u tom životnom toku.
Dokument ne tretira Studio kao centar sistema. Studio je jedna važna stanica unutar šireg
Selection
ekosistema: javni Gateway → Composer Gate → Onboarding → Control Plane →
Provisioning →
Identity
→ Experience Hub → Editor → Publish → Runtime.
Važna napomena o granici sigurnosti
Cloudflare potvrđuje ko je korisnik na perimetru, ali D1 ostaje jedini izvor poslovnog
identiteta,
tenant pripadnosti, uloge i statusa. Selection Session je jedina interna sesija, a
Router je
jedina
centralna tačka sprovođenja pravila.
1. Selection u jednoj rečenici
Selection vodi čoveka od prve posete javnom portalu do objavljenog, višestraničnog Website-a
kroz
jedan
neprekidan, kontrolisan i bezbedan tok u kojem svaka faza ima jednog jasnog vlasnika.
Pre Experience V2 modela, jedan Project je u praksi ličio na jednu web stranicu. Novi model
uvodi
pravi
Website sloj: Tenant poseduje Experience, a Experience poseduje Pages.
Fizička
Page i
dalje živi u tabeli projects, dok se njen sadržaj nalazi u
project_documents.
Time „Project“ više nije proizvodna ideja koju korisnik vidi, već tehnička fizička jedinica
stranice.
Tri identiteta moraju putovati zajedno kroz ceo privatni tok: tenant_id,
experience_id i project_id. To nije suvišna administracija; to je
dokaz
vlasništva. Sistem ne pita samo „postoji li Page“, već „pripada li ova Page ovom
Experience-u i
pripada
li taj Experience efektivnom tenantu ovog korisnika“.
3. Glavne zone sistema
Projekat / fajl
Uloga u toku
Šta prima
Šta predaje dalje
selection.rs
Javni Gateway i prvi kontakt
Anonimnog posetioca
Studio/Composer CTA
Composer Gate
Demo, objašnjenje proizvoda i ulaz u onboarding
Posetioca koji želi da razume Selection
Onboarding zahtev
admin.selection.rs
Master Control Plane
Odobren Cloudflare identitet Mastera
Approve / provisioning naredbu API-ju
api.selection.rs
Identitet, autorizacija, poslovna pravila i persistence
Zahteve Admina, Studija i Runtime-a
D1/R2 rezultat ili odbijanje
studio.selection.rs
Uređivanje Draft-a i upravljanje Website-om
Potvrđen Identity Context i Experience/Page kontekst
Save/Publish zahteve
runtime.selection.rs
Renderovanje javnog Live sadržaja
Live konfiguraciju
HTML/CSS/ponašanje za posetioca
contracts.selection.rs
Sovereign ugovori i validacija
Komponentne i runtime strukture
Jedinstvena pravila Studio/API/Runtime svetu
4. Faza A — Prvi kontakt: selection.rs
Korisnik još nema nalog, tenant, Experience ni Page. On je samo posetilac.
selection.rs zato
nema zadatak da ga autentifikuje ili da mu učita editor. Njegov posao je da objasni
vrednost:
Selection
nije alat za jednu landing stranicu, već put do kompletnog Website-a.
Kada klikne Studio CTA, korisnik ne bi trebalo da bude bačen u tehnički editor. Ulazi u
Composer
Gate —
kontrolisano predsoblje koje mu pokazuje Demo Experience, princip rada, primere i jasan
sledeći
korak.
Šta je UX cilj ove faze
Čovek razume šta dobija pre nego što ostavi podatke.
„Website“ je proizvodna reč; „Page“, „project_id“ i „Draft dokument“ ostaju
unutrašnji
tehnički
pojmovi.
Sledeći korak je uvek očigledan: pogledaj demo → pročitaj → pokreni onboarding.
5. Faza B — Composer Gate i Demo Experience
Composer Gate je marketinško-proizvodna granica između javnog interneta i procesa prijave.
Ovde
posetilac
vidi kako gotov Experience izgleda u realnom Runtime-u. To je važna razlika: demo ne obećava
nešto
apstraktno, već pokazuje isti mentalni model koji će kasnije uređivati u Studiju.
U Studio repozitorijumu postoje elementi kao što su hub.js,
css/hub.css
i
experiences/experience-engine.js, ali njihov pravi trenutak dolazi kasnije,
kada
korisnik
već ima identitet. Composer Gate može koristiti sličnu vizuelnu logiku, ali ne sme se
pomešati
sa
privatnim Experience Hub-om.
6. Faza C — Onboarding: zahtev, ne nalog
Kada korisnik klikne „Start“, otvara onboarding. On unosi ime, email, naziv, željeni
subdomain i
ostale
podatke. API ulaznu operaciju vodi kroz onboarding.js. Kritična stvar:
onboarding
ne mora
odmah da kreira aktivan tenant. On prvo upisuje zahtev sa statusom pending.
Onboarding forma → API onboarding.js → tenant_requests / zahtev → status = pending
Zašto je pending važan
Odvaja zainteresovanog posetioca od odobrenog korisnika.
Omogućava proveru emaila, subdomain-a, poslovnog konteksta ili plaćanja.
Sprečava da javna forma direktno kreira aktivne identitete i produkcione resurse.
Čuva jasan audit trag: ko je tražio pristup, kada i sa kojim podacima.
U ovoj fazi još ne postoji Selection Session. Korisnik nema „vizu“. Postoji samo njegov
zahtev u
poslovnom toku.
7. Faza D — Master ulazi u Control Plane
Master administrator otvara admin.selection.rs. Pre nego što Admin JavaScript
uopšte
dobije
priliku da zove Selection API, Cloudflare Access čuva vrata. Master email se potvrđuje na
Cloudflare
perimetru i tek tada se isporučuje Admin aplikacija.
Ova provera nije isto što i Selection autorizacija. Cloudflare kaže: „Ovaj browser je prošao
Access
proveru i predstavlja ovu osobu.“ Selection zatim mora u D1 da proveri da li ta osoba zaista
postoji kao
active/master. Bez tog D1 zapisa nema legitimnog Mastera.
Admin repozitorijum u životnom toku
Projekat / fajl
Uloga u toku
Šta prima
Šta predaje dalje
admin/index.html
Ulazni dokument Control Plane-a
Browser posle Cloudflare Access-a
Bootstrap i UI resurse
admin/bootstrap.js
Pokreće Admin aplikaciju i identity handshake
Zaštićeni browser kontekst
Potvrđen Admin boot ili grešku
admin/functions/auth/bootstrap.js
Edge/bootstrap pomoć pri autentifikaciji
Cloudflare assertion/cookie kontekst
Podatke potrebne klijentu za API handshake
admin/control-plane.js
Prikazuje pending zahteve i šalje administrativne akcije
Potvrđen Master kontekst
Approve/provision API pozive
admin/style.css
Vizuelni jezik Control Plane-a
HTML strukturu
Čitljiv i stabilan administrativni interfejs
admin/_headers
Security i delivery zaglavlja
Cloudflare Pages zahtev
CSP/cache/security ponašanje
8. Faza E — Approve i provisioning
Master u Control Plane-u otvara Pending Requests, pregleda zahtev i klikne Approve.
control-plane.js šalje eksplicitnu administrativnu naredbu API-ju. API ponovo
ne
veruje
samom dugmetu: router.js, identity.js, policy.js i
master.js moraju dokazati da zahtev dolazi iz legitimnog Master konteksta.
Tek posle odobrenja sistem kreira digitalni prostor korisnika. To nije samo Account. To je
relacijski
lanac čiji delovi moraju biti konzistentni i u istom vlasništvu:
Account → Tenant → Membership/Role
→ Default Experience → Default Home Page (projects)
→ Draft document (project_documents)
→ početni Foundation i dozvole
API fajlovi koji učestvuju u ovoj fazi
Projekat / fajl
Uloga u toku
Šta prima
Šta predaje dalje
api/app.js
Ulaz u Worker aplikaciju
HTTP zahtev
Prosleđuje ga router-u / odgovarajućem sloju
api/router.js
Jedina centralna dispatch/enforcement tačka
Metodu, putanju i Identity Context
Tačno jedan pipeline ili odbijanje
api/identity.js
Kreira jedinstveni Identity Context iz Session+D1
Validiranu sesiju i D1 stanje
effectiveTenantId, role, account/status
api/master.js
Master poslovne operacije
Master-authorized zahtev
Approve/provision akciju
api/policy.js
Centralna pravila dozvola i režima
Identity Context i operaciju
Dozvolu ili zabranu
api/onboarding.js
Životni ciklus prijave
Javni onboarding ili admin odluku
Pending/approved stanje
api/tenantResolver.js
Razrešava efektivnog tenanta
Identity Context i eventualni Master override
effectiveTenantId
api/pipelines/createPipeline.js
Kreira početne resurse
Odobren provisioning zahtev
Account/Tenant/Experience/Page/Draft redove
api/pipelines/experiencePipeline.js
Experience lifecycle
Tenant-scoped zahtev
Experience podatke ili lifecycle promenu
api/pipelines/tenantPipeline.js
Tenant operacije
Master/tenant kontekst
Tenant podatke i lifecycle rezultat
api/storage.js
Tehnički pristup D1/R2/KV skladištu
Validiran poslovni nalog
Persistiran ili učitan podatak
9. Canonical D1 šema — pravila koja čuvaju svet
migrations/0001_selection_canonical_schema.sql nije samo spisak CREATE TABLE
naredbi. To je
relacijski ustav Experience V2 sistema. Šema mora fizički da spreči nemoguća stanja tamo gde
SQL
to
može: duplikate, pogrešne reference, nepostojeće vlasnike i kršenje lifecycle pravila.
Najvažnije poslovne invarijante
Account mora postojati i biti active da bi identitet bio upotrebljiv.
Tenant je vlasnik Experience-a; Experience ne može pripadati drugom tenantu.
Page (projects red) pripada tačno jednom Experience-u i jednom tenantu.
project_documents sadržaj pripada tačno određenom
project_id-u
i
stanju Draft/Live.
Poslednja aktivna Page u aktivnom Experience-u ne sme se arhivirati ako bi
Experience
ostao bez
aktivne stranice.
Default Page i javna ruta moraju biti Experience-scoped, ne tenant-wide.
Slug je prezentacioni identifikator; project_id ostaje nepromenljivi
identitet
stranice.
10. „Viza“ — trenutak kada korisnik dobija pravo ulaska
Posle uspešnog provisioning-a sistem šalje WhatsApp poruku: nalog je odobren i Studio je
spreman.
Poruka
nije bezbednosni token; ona je poslovna potvrda i UX signal da je digitalni prostor kreiran.
Pravo
ulaska i dalje se dokazuje kroz Cloudflare Access + Selection Session + D1.
Ovaj trenutak je važan jer spaja administrativni svet i korisnički svet. Do tada je korisnik
bio
zahtev.
Od tada je aktivni član svog tenanta i može da započne prvi autorski tok.
11. Faza F — Prvi ulazak u studio.selection.rs
Korisnik otvara Studio. Cloudflare Access ponovo čuva perimetar, ali posle tog prolaza
Selection
mora da
izgradi sopstvenu internu sesiju. Browser učitava index.html, CSS i JavaScript.
Editor se
još ne pokreće. Prvo mora da se završi boot waterfall.
Studio root fajlovi — ko prvi ustaje
Projekat / fajl
Uloga u toku
Šta prima
Šta predaje dalje
studio/index.html
Isporučuje aplikacioni shell i script entry
Zaštićeni browser zahtev
bootstrap-client.js i stilove
studio/bootstrap-client.js
Jedini boot owner; odlučuje Hub/Page picker/Editor
URL, postojeću sesiju, Store/session kontekst
Tačno jednu sledeću UI fazu
studio/auth.js
Klijentski identity/session handshake
Cloudflare/Selection browser kontekst
Potvrđenu Selection identity informaciju
studio/app.js
Pokreće puni editor tek kada boot to dozvoli
Identity + experienceId + projectId
Registry, Store, Shell, services i simulator
studio/hub.js
Experience Hub i izbor Website-a
Listu tenantovih Experience-a
Izabrani experienceId / nastavak boot-a
studio/store.js
Javni bridge ka Store sistemu
Akcije i hydration podatke
Stabilan Store API za ostatak aplikacije
studio/history.js
Legacy/javni history bridge gde je potreban
Promene dokumenta
Undo/redo integraciju
studio/fetch-contract.js
Mirroring/sinhronizacija ugovora
Centralni contracts izvor
Lokalnu validacionu kopiju
studio/_headers
Security, CSP i cache pravila
Browser request
Bezbednu isporuku Studija
12. Boot waterfall — kako Studio odlučuje šta korisnik vidi
bootstrap-client.js je recepcija i saobraćajni policajac Studija. Njegova
najveća
vrednost
je što je jedini vlasnik boot odluke. app.js ne sme samostalno da se importuje
iz
hub.js niti da pokreće paralelni editor. Time je uklonjen raniji circular boot
i
duplo
inicijalizovanje Kernel-a.
Pročitaj URL parametre: experienceId i projectId/Page
identitet.
Proveri postojeći Store/session kontekst ako URL nije kompletan.
Pokreni identity handshake i potvrdi Selection Session.
Ako experienceId ne postoji, prikaži Experience Hub.
Ako Experience postoji, ali Page nije izabrana, prikaži Page picker/workspace izbor.
Ako postoje Experience i Page, pozovi startEditor: initStudioV2 iz
app.js.
Editor se inicijalizuje jednom; nema drugog skrivenog boot vlasnika.
URL + Session + D1 identity
→ nema Experience: Experience Hub
→ ima Experience, nema Page: Page picker
→ ima Experience + Page: Editor
13. Selection Session — unutrašnji pasoš
Cloudflare Access assertion nije dovoljna za rad unutar platforme. Studio preko
auth.js i
odgovarajućeg bootstrap endpointa traži Selection Session. API proverava Cloudflare dokaz,
pronalazi
nalog u D1 i izdaje Selection JWT u HttpOnly cookie-ju. Posle toga interne rute koriste
Selection
Session, ne Cloudflare kao poslovni autoritet.
API auth lanac
Projekat / fajl
Uloga u toku
Šta prima
Šta predaje dalje
api/auth/cloudflare-verifier.js
Verifikuje Cloudflare dokaz na perimetru
CF assertion/header/cookie
Potvrđen spoljašnji identitet
api/auth/jwt.js
Potpisuje i proverava Selection JWT
Claims i tajni ključ
Validan token ili grešku
api/auth/session.js
Čita/izdaje/poništava Selection Session cookie
HTTP zahtev/odgovor
Internu sesiju
api/auth/tenant.js
Pomoćni tenant auth kontekst
Claims/D1 podatke
Tenant-scoped auth rezultat
api/pipelines/authPipeline.js
Orkestrira session handshake
Cloudflare dokaz
Selection Session / identity response
api/claims.js
Definiše i čita claims strukturu
JWT payload
Standardizovane claims
contracts/identity/claims-schema.json
Ugovor o obliku claims-a
Identity model
Validaciono pravilo za sve slojeve
14. D1 revalidacija — zašto JWT sam nije dovoljan
JWT može biti kriptografski ispravan, a da je korisnik u međuvremenu deaktiviran, prebačen,
opozvan ili
mu je promenjena uloga. Zbog toga identity.js posle provere potpisa radi D1
lookup.
Tek D1
određuje trenutno stanje.
Rezultat je jedinstveni Identity Context. Pipeline-i ga ne rekonstruišu na svoj način. On
nosi
accountId, role, tenantId,
effectiveTenantId,
status i relevantne dozvole. Time se sprečava paralelna identity logika po
različitim
rutama.
15. Experience Hub — korisnik bira Website, ne „projekat“
Kada identitet prođe, ali u kontekstu nema experienceId,
bootstrap-client.js
otvara hub.js. Hub preko services/experience-orchestrator.js i
services/api-service.js traži Experience-e koji pripadaju
effectiveTenantId-u.
Korisnik vidi kartice svojih Website-ova.
Ovo je ključni UX pomak Experience V2: korisnik prvo bira Website, a tek unutar njega Page.
Time
Top Bar,
Page picker, Action Card interni linkovi i lifecycle pravila moraju biti Experience-scoped.
Relevantne Studio usluge
Projekat / fajl
Uloga u toku
Šta prima
Šta predaje dalje
services/api-service.js
Jedna klijentska kapija ka API-ju
Identity/session + request parametre
JSON rezultat ili standardizovanu grešku
services/experience-orchestrator.js
Orkestrira list/create/select Experience toka
Tenant identity i UI događaj
Izabrani Experience kontekst
services/workspace-service.js
Upravlja Page/workspace kontekstom unutar Experience-a
experienceId + projectId
Aktivni workspace/Page
events/studio-events.js
Događajni ugovor između modula
UI/store/system događaje
Labavo povezane reakcije
css/hub.css
Vizuelni izgled Experience Hub-a
Hub DOM
Jasne Website kartice
css/explorer.css
Page/Experience explorer prikaz
Workspace podatke
Navigacioni UI
16. Učitavanje Page — prvi pravi privatni sadržaj
Kada korisnik izabere Experience i Page, app.js pokreće editor.
services/api-service.js poziva privatni load endpoint sa
experienceId
i
projectId. Router bira loadPipeline.js. Pipeline ne radi samo
SELECT
po
project_id-u: mora da potvrdi triple-scope vlasništvo i lifecycle stanje.
project_id pripada baš tom Experience-u i istom tenantu.
Experience i Page imaju status koji dozvoljava učitavanje u editor.
Učitava se Draft stanje; Live nikada nije radna kopija editora.
Odgovor ne može „preskočiti“ ownership samo zato što project_id
postoji.
17. Store — radna memorija Website-a
Po povratku Draft dokumenta browser preuzima posao. Store hidrira dokument i postaje jedini
radni
model
trenutne Page. Važno je da Store čuva experienceId uz projectId,
jer
svaka
kasnija operacija — Save, Upload, Publish, Page list, Action Card link — mora znati puni
kontekst.
Projekat / fajl
Uloga u toku
Šta prima
Šta predaje dalje
store/index.js
Javni ulaz u Store
Boot/hydration i UI akcije
State API
store/state.js
Definiše početno stanje
Foundation, capsule i workspace podatke
Početni state
store/reducer.js
Jedini deterministički menjač stanja
Akcije
Novi state
store/factories.js
Pravi nove capsule instance
Tip komponente i defaults
Validnu početnu instancu
store/history.js
Undo/redo istorija
Promene state-a
Prethodno/sledeće stanje
store/migrations.js
Podizanje starijih Draft formata
Stari dokument
Trenutni kompatibilni oblik
store/presets.js
Početni dokument/preset modeli
Izbor preseta
Foundation/capsule vrednosti
store/utils.js
Čiste pomoćne transformacije
State vrednosti
Normalizovane podatke
18. Contracts — jedan jezik za Studio, API i Runtime
Contracts repozitorijum je zajednički ustav sadržaja. Bez njega bi Studio mogao da sačuva
polje
koje
Runtime ne razume, ili bi Runtime očekivao strukturu koju API ne validira.
fetch-contract.js u Studio i Runtime projektima služi da centralna pravila budu
lokalno
dostupna, ali centralni izvor ostaje contracts.selection.rs.
Projekat / fajl
Uloga u toku
Šta prima
Šta predaje dalje
contracts/validator.js
Centralna validaciona logika
Dokument/komponentu
Validno ili preciznu grešku
contracts/version.json
Verzija ugovora
Deploy/build
Kompatibilnost
contracts/api/endpoints.json
Ugovor privatnih/javnih endpointa
API klijente i router
Standardizovane rute
contracts/components/*.js
Komponentni poslovni ugovori
Foundation/Hero/Room/Action podatke
Dozvoljena polja/default semantiku
contracts/schemas/registry-schema.json
Ugovor Component Registry-a
Manifest/registry definiciju
Validan registry
contracts/runtime/runtime-contract.json
Ugovor Live dokumenta
Publish output
Runtime-kompatibilan payload
contracts/runtime/block-schema.json
Osnovna block/capsule struktura
Komponentnu instancu
Validan blok
19. Registry — katalog mogućnosti
Registry odgovara na pitanje: „Koje komponente ova verzija Studija zna da uređuje?“ Ne
renderuje
stranicu
i ne čuva podatke. On registruje capsule module, njihove editore, defaults i contracts.
app.js pokreće registry/boot.js; loader.js učitava
module
prema
manifest.js; index.js izlaže gotov katalog.
Projekat / fajl
Uloga u toku
Šta prima
Šta predaje dalje
registry/manifest.js
Deklariše dostupne module
Build verziju
Spisak komponenti
registry/loader.js
Dinamički učitava module
Manifest stavke
Učitane component module
registry/index.js
Javni registry API
Učitane module
Lookup po type-u
registry/boot.js
Orkestrira registraciju pri startu
Manifest i loader
Spreman registry
engine/component-registry.js
Runtime mapa Studio komponenti
Component module
Editor/default/contract lookup
20. Komponenta kao suverena kapsula
Svaka komponenta u Studio projektu ima mali zatvoren životni krug. defaults.js
definiše kako
izgleda nova instanca; editor.js gradi Inspector UI; index.js
registruje
komponentu. Foundation je poseban trajni DNA sloj, dok Hero, Room Card i Action Card
predstavljaju
sadržajne kapsule.
Projekat / fajl
Uloga u toku
Šta prima
Šta predaje dalje
components/foundation/defaults.js
Početni DNA Website-a
Preset/novi dokument
Foundation state
components/foundation/presets.js
Gotovi vizuelni sistemi
Izbor korisnika
Palette/typography/canvas vrednosti
components/foundation/controller.js
Posebna Foundation pravila
Inspector promene
Kontrolisanu izmenu trajnog sloja
components/foundation/editor.js
Foundation Inspector
Foundation state
UI kontrole
components/hero/*
Hero capsule authoring
Hero state
Naslov, media i layout izmene
components/room_card/*
Room Card authoring
Room Card state
Karticu sa sadržajem/slikama
components/action_card/*
Interna/spoljna navigaciona capsule
Link/reference izbor
CTA ili Page link
21. Experience i Blueprint sloj — „tri klika do Website-a“
blueprints/blueprint-engine.js, blueprints/party.js,
experiences/experience-engine.js i experiences/party_invitation.js
predstavljaju početak višeg nivoa automatizacije. Komponenta je građevinska jedinica, Page
je
ekran,
Experience je Website, a Blueprint je recept koji u nekoliko koraka može da kreira početni
komplet
stranica i kapsula.
Izbor Experience tipa → Blueprint → kreiranje Website strukture
→ Home + dodatne Pages + početne capsule → korisnik odmah uređuje
Upravo ovde proizvodna poruka „3 Click Website“ postaje realna: klik 1 — izaberi Experience;
klik
2 —
izaberi blueprint/preset; klik 3 — kreiraj. Iza tri klika radi kompletan relational,
contract,
persistence i runtime sistem.
22. UI Shell — ono što korisnik zove „Studio“
Kada su Store i Registry spremni, shell/ui-shell.js sklapa vizuelni editor.
topbar.js prikazuje Website/Page kontekst i glavne akcije.
timeline.js
prikazuje redosled capsule-a. inspector-host.js otvara editor iz Registry-a za
trenutno
selektovanu komponentu.
Projekat / fajl
Uloga u toku
Šta prima
Šta predaje dalje
shell/ui-shell.js
Sastavlja editor layout
Store i servise
Top Bar/Timeline/Inspector/Canvas
shell/topbar.js
Globalne akcije i kontekst
Experience/Page status
Save/Publish/navigation događaje
shell/timeline.js
Redosled i izbor capsule-a
Store capsules
Selektovanu komponentu/reorder
shell/inspector-host.js
Učitava odgovarajući component editor
Registry lookup + selection
Inspector kontrole
css/topbar.css
Top Bar vizuelni sloj
Topbar DOM
Jasne akcije
css/workspace.css
Glavni editor raspored
Shell DOM
Radni prostor
css/variables.css
Design tokeni Studija
CSS module
Dosledan izgled
css/splash.css
Boot/loading stanje
Boot fazu
Kontrolisani prelaz do Hub/Editor-a
23. Simulator — Draft izgleda kao Website pre objave
services/simulator-bridge.js je granica između Store-a i preview Runtime-a. Kada
reducer
promeni Foundation ili capsule, bridge šalje novo Draft stanje simulatoru.
preview-runtime.js u Runtime projektu prima kontrolisani preview protokol i
renderuje isti
mentalni model kao javni Runtime, ali iz Draft-a i unutar Studija.
Inspector promena → Store action → reducer → simulator-bridge
→ preview-runtime.js → render-engine → korisnik odmah vidi rezultat
24. Upload — mediji pripadaju tačnoj Page
services/upload-service.js šalje fajl zajedno sa tenant_id,
experience_id i project_id kontekstom. API router bira
assetPipeline.js, koji proverava ownership pre pisanja u R2. Draft asset ostaje
privatan;
Publish odlučuje šta postaje javno i pod kojim stabilnim javnim putem.
Projekat / fajl
Uloga u toku
Šta prima
Šta predaje dalje
studio/services/upload-service.js
Klijentski upload orkestrator
Fajl + puni kontekst
Asset reference
api/pipelines/assetPipeline.js
Ownership, validacija i R2 operacija
Identity + experienceId + projectId + fajl
Bezbedno upisan asset
api/storage.js
R2 primitive
Canonical asset key
Persistiran objekat
runtime/engine/asset-manager.js
Live upravljanje assetima
Reference iz Live config-a
Resolved asset za renderer
runtime/adapters/dom/asset-resolver.js
Pretvara reference u URL
Asset reference i runtime context
Javni ili preview URL
25. Autosave i ručni Save
autosave-controller.js posmatra relevantne Store promene, debounce-uje ih i
poziva
api-service.js. Save zahtev mora sadržati experienceId,
projectId
i Draft payload. savePipeline.js zatim proverava ownership, validira dokument
ugovorom i
upisuje samo Draft stanje u D1.
Store changed → autosave-controller.js → api-service.js POST /api/save
→ router.js → savePipeline.js → contract validation → project_documents state=draft
26. Klik na Publish — prelazak iz privatnog u javno
Publish je granica dva sveta. Pre klika postoji autorski Draft. Posle uspešnog publish-a
postoji
immutable javna predstava koju Runtime sme da čita. Studio ne piše Live direktno; on samo
šalje
nameru
API-ju.
Top Bar emituje Publish događaj za aktivni Experience i Page.
api-service.js šalje tenant-safe zahtev sa experienceId i
projectId.
router.js potvrđuje rutu, metod i Identity Context.
publishPipeline.js ponovo učitava canonical Draft iz D1 — ne veruje slepo
browser
payload-u.
Pipeline proverava triple ownership i lifecycle stanje.
contracts/validator.js validira Draft.
Publish transformacija pravi Runtime-kompatibilni Live dokument.
Live stanje se upisuje u D1 i/ili canonical javni R2 config prema ugovoru.
Javni asset reference se razrešavaju za Runtime delivery plane.
API vraća uspeh tek kada je javni artefakt konzistentan.
DRAFT (D1 SSOT) → reload → validate → transform → LIVE (D1 + R2 public config)
→ runtime.selection.rs → Live Website
27. Publish pipeline — vlasništvo i audit tačke
publishPipeline.js treba auditovati prema canonical Experience V2 ugovoru, a ne
prepisivati
naslepo. Najvažnije je dokazati da svaki SELECT/UPDATE/INSERT koristi
effectiveTenantId + experienceId + projectId gde je to poslovno potrebno.
Strogi uslovi publikovanja
Publish ne sme da pronađe Page samo po project_id-u bez Experience
pripadnosti.
Ne sme da objavi arhiviranu Page ili Page iz arhiviranog Experience-a.
Default Page i URL mapping moraju ostati u istom Experience-u.
Live output mora proći runtime-contract validaciju.
R2 ključ i javni URL moraju koristiti stabilne identitete, ne promenljivi title.
28. Runtime — javni svet bez editora
Kada korisnik posle Publish-a klikne „View Live“, otvara javnu URL putanju. Runtime nema
Selection
Session, Inspector, Timeline, Save ni Draft. On prihvata samo javni GET tok, razrešava
tenant/Experience/Page iz hosta i putanje, učitava isključivo Live config i renderuje ga.
Studio komponenta i Runtime komponenta dele isti ugovor, ali nemaju istu odgovornost. Studio
editor menja
podatke; Runtime renderer samo čita validan Live state i prikazuje ga. Zato svaka Runtime
komponenta ima
index.js za registraciju i renderer.js za javni prikaz.
Projekat / fajl
Uloga u toku
Šta prima
Šta predaje dalje
runtime/components/foundation/renderer.js
Primena Website DNA
Foundation Live state
Globalni izgled
runtime/components/hero/renderer.js
Javni Hero
Hero Live state
Naslov/media/CTA DOM
runtime/components/room_card/renderer.js
Javna Room Card
Room Card state
Kartica i dozvoljene interakcije
runtime/components/action_card/renderer.js
Javna navigaciona kartica
Reference/link state
Interni Page ili spoljni link
33. Behavior sistem — život posle rendera
Nakon osnovnog DOM rendera behavior-manager.js aktivira ponašanja koja su
dozvoljena
ugovorom. FadeInBehavior dodaje ulaznu animaciju;
AmbientAudioBehavior
kontroliše audio. behavior-registry.js određuje koje ponašanje postoji,
behavior.js daje bazni model, a event-bus povezuje događaje.
34. Klik „Live“ — šta korisnik doživljava, a šta sistem radi
KORISNIK VIDI: Publish → Success → View Live → Website
SISTEM RADI: Identity → Ownership → Draft reload → Contract validation
→ Live transform → D1/R2 write → Public route resolution → Runtime render
35. Arhiviranje i vraćanje — životni ciklus posle prvog publish-a
Website nije statičan zauvek. Page i Experience mogu biti arhivirani i vraćeni, ali lifecycle
mora
poštovati relacijske uslove. Page archive/restore pripada API/Workspace sloju, dok capsule
delete
pripada Store-u. To su dve različite vrste „brisanja“.
Page archive proverava aktivne Pages unutar istog experience_id, ne na
nivou
celog
tenant-a.
Ne sme se arhivirati jedina aktivna Page aktivnog Experience-a bez definisanog prelaza.
Experience archive mora definisati šta se dešava sa njegovim Pages, default rutom i
javnim
Live
sadrajem.
Restore mora ponovo proveriti slug/UNIQUE konflikte i validno vlasništvo.
36. Bezbednosni model po slojevima
Sloj
Projekat / tehnologija
Uloga i Granica Odgovornosti
Perimetar
Cloudflare Access
Perimetarska potvrda osobe/domene. Ko pokušava da uđe.
Sesija
Selection Session
Interna autentifikovana sesija (HttpOnly JWT).
Identitet
D1 Identity
Jedini izvor poslovne istine (Account, membership, role, status).
Protokol
Router
Jedina centralna enforcement tačka za sve API pozive.
Operacija
Pipeline
Poslovna operacija i triple-scope ownership proveravanje.
Skladište
Storage
Persistencija bez poslovnog odlučivanja (D1/R2/KV).
Prikaz
Runtime
Javno čitanje Live stanja bez ikakvih privatnih ovlašćenja.
37. Zašto četiri odvojena repozitorijuma nisu komplikacija
Odvojenost Studio, Runtime, API, Contracts i Admin svetova nije slučajna. Ona sprečava da
editor
odlučuje
ko je korisnik, da Runtime dobije write mogućnosti, da Admin renderuje Website ili da API
zna
kako
izgleda Inspector.
Admin je Control Plane.
API je Kernel: identitet, autorizacija, ownership, persistence i
publish.
Studio je Authoring Plane: uređuje Draft i simulira ga.
Runtime je Delivery Plane: čita samo Live i renderuje.
Contracts su zajednički jezik koji drži sve slojeve kompatibilnim.
38. Kompletna mapa jednog zahteva — od Inspector promene do Live piksela
Korisnik promeni naslov Hero capsule u Inspectoru.
components/hero/editor.js emituje Studio događaj ili Store akciju.
store/reducer.js pravi novi state i history beleži promenu.
simulator-bridge.js šalje Draft preview Runtime-u.
preview-runtime.js i render-engine.js odmah prikažu novi
naslov.
autosave-controller.js posle debounce-a pozove api-service.js.
Najveća promena Experience V2 nije nova kolona experience_id. Najveća promena je
mentalni
model: Selection sada zaista poseduje nivo Website-a. Page je deo Website-a, a ne ceo
proizvod.
Zbog
toga identitet, ownership, boot, lifecycle, navigacija, publish i runtime ruta svi moraju da
razumeju
isti lanac Tenant → Experience → Page.
Kada korisnik napravi Website u tri klika, ta jednostavnost nije nastala zato što je sistem
jednostavan.
Nastala je zato što je složenost raspoređena u jasne slojeve, svaki sa jednim vlasnikom i
jednim
ugovorom. To je razlika između demonstracije i platforme.
Selection ne pravi samo stranice. Selection izdaje digitalni prostor, vodi korisnika
kroz
njega i
objavljuje ceo Website.
Canonical Master Map — Experience V2
Selection Architecture Atlas
Technical and process manifesto — from first visit to Live Website
0. How to read this document
This document follows a single story: a person sees Selection for the first time, submits a
request, gets approved, receives their digital space, enters the Studio, selects an
Experience, edits a Page, saves a Draft, clicks Publish, and finally opens the real public
Website. Every script and every folder is explained at the moment it naturally appears in
that lifecycle.
The document does not treat the Studio as the center of the system. The Studio is one
important stop within the broader Selection ecosystem: public Gateway → Composer
Gate → Onboarding → Control Plane → Provisioning → Identity → Experience Hub → Editor →
Publish → Runtime.
Important note on the security boundary
Cloudflare confirms who the user is at the perimeter, but D1 remains the sole source of
business identity, tenant ownership, roles, and status. The Selection Session is the
only internal session, and the Router is the only central point for enforcing rules.
1. Selection in one sentence
Selection guides a person from the first visit to the public portal to a published,
multi-page Website through a single continuous, controlled, and secure flow where each phase
has one clear owner.
Before the Experience V2 model, a Project in practice looked like a single webpage. The new
model introduces a true Website layer: Tenant owns the Experience, and the
Experience owns Pages. The physical Page still lives in the
projects table, while its content is located in project_documents.
Thus, "Project" is no longer the product idea the user sees, but the technical physical unit
of the page.
Three identities must travel together through the entire private flow:
tenant_id, experience_id, and project_id. This is not
redundant administration; it is proof of ownership. The system doesn't just ask "does the
Page exist?", but "does this Page belong to this Experience and does that Experience belong
to the effective tenant of this user?".
3. Main system zones
Project / File
Role in the flow
What it receives
What it passes on
selection.rs
Public Gateway and first contact
Anonymous visitor
Studio/Composer CTA
Composer Gate
Demo, product explanation, and entry to onboarding
Visitor who wants to understand Selection
Onboarding request
admin.selection.rs
Master Control Plane
Approved Cloudflare identity of the Master
Approve / provisioning command to the API
api.selection.rs
Identity, authorization, business rules, and persistence
Requests from Admin, Studio, and Runtime
D1/R2 result or rejection
studio.selection.rs
Editing the Draft and managing the Website
Confirmed Identity Context and Experience/Page context
Save/Publish requests
runtime.selection.rs
Rendering public Live content
Live configuration
HTML/CSS/behavior for the visitor
contracts.selection.rs
Sovereign contracts and validation
Component and runtime structures
Unified rules for the Studio/API/Runtime world
4. Phase A — First contact: selection.rs
The user doesn't have an account, tenant, Experience, or Page yet. They are just a visitor.
selection.rs therefore has no task to authenticate them or load the editor. Its
job is to explain the value: Selection is not a tool for a single landing page, but a path
to a complete Website.
When they click the Studio CTA, the user shouldn't be thrown into a technical editor. They
enter the Composer Gate — a controlled antechamber that shows them a Demo Experience, the
working principle, examples, and a clear next step.
What is the UX goal of this phase
The person understands what they are getting before leaving their data.
"Website" is a product word; "Page", "project_id", and "Draft document" remain
internal technical terms.
The next step is always obvious: view the demo → read → start onboarding.
5. Phase B — Composer Gate and Demo Experience
Composer Gate is the marketing-product boundary between the public internet and the signup
process. Here, the visitor sees what a finished Experience looks like in a real Runtime.
This is an important distinction: the demo doesn't promise something abstract, but shows the
same mental model they will later edit in the Studio.
In the Studio repository, there are elements like hub.js,
css/hub.css, and experiences/experience-engine.js, but their real
moment comes later, when the user already has an identity. The Composer Gate can use similar
visual logic, but must not be confused with the private Experience Hub.
6. Phase C — Onboarding: request, not account
When the user clicks "Start", the onboarding opens. They enter their name, email, title,
desired subdomain, and other data. The API routes the input operation through
onboarding.js. Critical point: onboarding does not have to immediately create
an active tenant. It first writes a request with a pending status.
Onboarding form → API onboarding.js → tenant_requests / request → status = pending
Why pending is important
Separates an interested visitor from an approved user.
Enables verification of the email, subdomain, business context, or payment.
Prevents the public form from directly creating active identities and production
resources.
Keeps a clear audit trail: who requested access, when, and with what data.
At this phase, the Selection Session does not exist yet. The user has no "visa". There is
only their request in the business flow.
7. Phase D — Master enters the Control Plane
The Master administrator opens admin.selection.rs. Before the Admin JavaScript
even gets a chance to call the Selection API, Cloudflare Access guards the door. The Master
email is verified at the Cloudflare perimeter, and only then is the Admin application
delivered.
This check is not the same as Selection authorization. Cloudflare says: "This browser passed
the Access check and represents this person." Selection then must check in D1 if that person
actually exists as active/master. Without that D1 record, there is no legitimate Master.
Admin repository in the lifecycle
Project / File
Role in the flow
What it receives
What it passes on
admin/index.html
Entry document of the Control Plane
Browser after Cloudflare Access
Bootstrap and UI resources
admin/bootstrap.js
Starts the Admin application and identity handshake
Protected browser context
Confirmed Admin boot or error
admin/functions/auth/bootstrap.js
Edge/bootstrap authentication assistance
Cloudflare assertion/cookie context
Data needed by the client for the API handshake
admin/control-plane.js
Displays pending requests and sends administrative actions
Confirmed Master context
Approve/provision API calls
admin/style.css
Visual language of the Control Plane
HTML structure
Readable and stable administrative interface
admin/_headers
Security and delivery headers
Cloudflare Pages request
CSP/cache/security behavior
8. Phase E — Approve and provisioning
Master in the Control Plane opens Pending Requests, reviews the request, and clicks Approve.
control-plane.js sends an explicit administrative command to the API. The API
again doesn't trust the button alone: router.js, identity.js,
policy.js, and master.js must prove the request comes from a
legitimate Master context.
Only after approval does the system create the user's digital space. This isn't just an
Account. It is a relational chain whose parts must be consistent and under the same
ownership:
Account → Tenant → Membership/Role
→ Default Experience → Default Home Page (projects)
→ Draft document (project_documents)
→ initial Foundation and permissions
API files participating in this phase
Project / File
Role in the flow
What it receives
What it passes on
api/app.js
Entry point for the Worker application
HTTP request
Forwards it to the router / appropriate layer
api/router.js
The single central dispatch/enforcement point
Method, path, and Identity Context
Exactly one pipeline or rejection
api/identity.js
Creates a unique Identity Context from Session+D1
Validated session and D1 state
effectiveTenantId, role, account/status
api/master.js
Master business operations
Master-authorized request
Approve/provision action
api/policy.js
Central permission and mode rules
Identity Context and operation
Permission or denial
api/onboarding.js
Signup lifecycle
Public onboarding or admin decision
Pending/approved state
api/tenantResolver.js
Resolves the effective tenant
Identity Context and potential Master override
effectiveTenantId
api/pipelines/createPipeline.js
Creates initial resources
Approved provisioning request
Account/Tenant/Experience/Page/Draft rows
api/pipelines/experiencePipeline.js
Experience lifecycle
Tenant-scoped request
Experience data or lifecycle change
api/pipelines/tenantPipeline.js
Tenant operations
Master/tenant context
Tenant data and lifecycle result
api/storage.js
Technical access to D1/R2/KV storage
Validated business payload
Persisted or loaded data
9. Canonical D1 schema — rules that protect the world
migrations/0001_selection_canonical_schema.sql is not just a list of CREATE
TABLE statements. It is the relational constitution of the Experience V2 system. The schema
must physically prevent impossible states where SQL can: duplicates, incorrect references,
nonexistent owners, and lifecycle rule violations.
Most important business invariants
Account must exist and be active for the identity to be usable.
Tenant owns the Experience; an Experience cannot belong to another tenant.
Page (projects row) belongs to exactly one Experience and one tenant.
project_documents content belongs to a specific project_id
and Draft/Live state.
The last active Page in an active Experience must not be archived if it would leave
the Experience without an active page.
Default Page and public route must be Experience-scoped, not tenant-wide.
Slug is a presentational identifier; project_id remains the immutable
page identity.
10. "Visa" — the right of entry
After successful provisioning, the system sends a WhatsApp message: the account is approved
and the Studio is ready. The message is not a security token; it is a business confirmation
and a UX signal that the digital space has been created. The right of entry is still proven
through Cloudflare Access + Selection Session + D1.
This moment is important because it connects the administrative world and the user world.
Until then, the user was a request. From then on, they are an active member of their tenant
and can begin their first authoring flow.
11. Phase F — First entry into studio.selection.rs
The user opens the Studio. Cloudflare Access again guards the perimeter, but after that pass,
Selection must build its own internal session. The browser loads index.html,
CSS, and JavaScript. The editor does not boot yet. The boot waterfall must finish first.
Studio root files — who wakes up first
Project / File
Role in the flow
What it receives
What it passes on
studio/index.html
Delivers the application shell and script entry
Protected browser request
bootstrap-client.js and styles
studio/bootstrap-client.js
The single boot owner; decides Hub/Page picker/Editor
URL, existing session, Store/session context
Exactly one next UI phase
studio/auth.js
Client identity/session handshake
Cloudflare/Selection browser context
Confirmed Selection identity information
studio/app.js
Starts the full editor only when boot allows it
Identity + experienceId + projectId
Registry, Store, Shell, services, and simulator
studio/hub.js
Experience Hub and Website selection
List of tenant's Experiences
Selected experienceId / continuation of boot
studio/store.js
Public bridge to the Store system
Actions and hydration data
Stable Store API for the rest of the app
studio/history.js
Legacy/public history bridge where needed
Document changes
Undo/redo integration
studio/fetch-contract.js
Mirroring/synchronization of contracts
Central contracts source
Local validation copy
studio/_headers
Security, CSP, and cache rules
Browser request
Secure delivery of the Studio
12. Boot waterfall — display decision
bootstrap-client.js is the reception and traffic cop of the Studio. Its greatest
value is being the sole owner of the boot decision. app.js must not import
itself from hub.js or start a parallel editor. This removes the previous
circular boot and double Kernel initialization.
Read URL parameters: experienceId and projectId/Page identity.
Check existing Store/session context if the URL is incomplete.
Start identity handshake and confirm the Selection Session.
If experienceId does not exist, show the Experience Hub.
If Experience exists, but Page is not selected, show the Page picker/workspace
selection.
If both Experience and Page exist, call startEditor: initStudioV2 from
app.js.
Editor is initialized once; there is no other hidden boot owner.
URL + Session + D1 identity
→ no Experience: Experience Hub
→ has Experience, no Page: Page picker
→ has Experience + Page: Editor
13. Selection Session — internal passport
A Cloudflare Access assertion is not enough to work within the platform. The Studio requests
a Selection Session via auth.js and the corresponding bootstrap endpoint. The
API verifies the Cloudflare proof, finds the account in D1, and issues a Selection JWT in an
HttpOnly cookie. After that, internal routes use the Selection Session, not Cloudflare, as
the business authority.
API auth chain
Project / File
Role in the flow
What it receives
What it passes on
api/auth/cloudflare-verifier.js
Verifies the Cloudflare proof at the perimeter
CF assertion/header/cookie
Confirmed external identity
api/auth/jwt.js
Signs and verifies the Selection JWT
Claims and secret key
Valid token or error
api/auth/session.js
Reads/issues/revokes the Selection Session cookie
HTTP request/response
Internal session
api/auth/tenant.js
Auxiliary tenant auth context
Claims/D1 data
Tenant-scoped auth result
api/pipelines/authPipeline.js
Orchestrates the session handshake
Cloudflare proof
Selection Session / identity response
api/claims.js
Defines and reads the claims structure
JWT payload
Standardized claims
contracts/identity/claims-schema.json
Contract on the shape of claims
Identity model
Validation rule for all layers
14. D1 identity revalidation
A JWT can be cryptographically valid while the user has been deactivated, transferred,
revoked, or had their role changed in the meantime. That is why identity.js
performs a D1 lookup after verifying the signature. Only D1 determines the current state.
The result is a unique Identity Context. Pipelines do not reconstruct it in their own way. It
carries accountId, role, tenantId,
effectiveTenantId, status, and relevant permissions. This prevents
parallel identity logic across different routes.
15. Experience Hub — Website selection
When identity passes, but the context lacks an experienceId,
bootstrap-client.js opens hub.js. The Hub uses
services/experience-orchestrator.js and services/api-service.js to
look for Experiences belonging to the effectiveTenantId. The user sees cards of
their Websites.
This is the key UX shift of Experience V2: the user first chooses a Website, and only then a
Page within it. Thus, the Top Bar, Page picker, Action Card internal links, and lifecycle
rules must be Experience-scoped.
Relevant Studio services
Project / File
Role in the flow
What it receives
What it passes on
services/api-service.js
A single client gateway to the API
Identity/session + request parameters
JSON result or standardized error
services/experience-orchestrator.js
Orchestrates the list/create/select Experience flow
Tenant identity and UI event
Selected Experience context
services/workspace-service.js
Manages the Page/workspace context within the Experience
experienceId + projectId
Active workspace/Page
events/studio-events.js
Event contract between modules
UI/store/system events
Loosely coupled reactions
css/hub.css
Visual appearance of the Experience Hub
Hub DOM
Clear Website cards
css/explorer.css
Page/Experience explorer view
Workspace data
Navigational UI
16. Page loading — private content
When the user selects an Experience and a Page, app.js starts the editor.
services/api-service.js calls the private load endpoint with
experienceId and projectId. The Router selects
loadPipeline.js. The pipeline doesn't just perform a SELECT by
project_id: it must confirm triple-scope ownership and lifecycle state.
Studio: experienceId + projectId
→ router.js
→ loadPipeline.js
→ projects ownership check
→ project_documents state=draft
→ Draft JSON back to Studio
What loadPipeline must prove
effectiveTenantId owns the requested Experience.
project_id belongs to exactly that Experience and the same tenant.
Experience and Page have a status that allows loading into the editor.
The Draft state is loaded; Live is never the working copy of the editor.
The response cannot "bypass" ownership just because the project_id
exists.
17. Store — Website working memory
Upon the return of the Draft document, the browser takes over the job. The Store hydrates the
document and becomes the sole working model of the current Page. It is important that the
Store keeps experienceId alongside projectId, because every
subsequent operation — Save, Upload, Publish, Page list, Action Card link — must know the
full context.
Project / File
Role in the flow
What it receives
What it passes on
store/index.js
Public entry to the Store
Boot/hydration and UI actions
State API
store/state.js
Defines the initial state
Foundation, capsule, and workspace data
Initial state
store/reducer.js
The only deterministic state changer
Actions
New state
store/factories.js
Creates new capsule instances
Component type and defaults
Valid initial instance
store/history.js
Undo/redo history
State changes
Previous/next state
store/migrations.js
Upgrading older Draft formats
Old document
Current compatible format
store/presets.js
Initial document/preset models
Preset selection
Foundation/capsule values
store/utils.js
Pure helper transformations
State values
Normalized data
18. Contracts — shared constitution
The Contracts repository is the shared constitution of the content. Without it, the Studio
could save a field that the Runtime doesn't understand, or the Runtime would expect a
structure the API doesn't validate. fetch-contract.js in the Studio and Runtime
projects serves to make central rules locally available, but the central source remains
contracts.selection.rs.
Project / File
Role in the flow
What it receives
What it passes on
contracts/validator.js
Central validation logic
Document/component
Valid or a precise error
contracts/version.json
Contract version
Deploy/build
Compatibility
contracts/api/endpoints.json
Contract of private/public endpoints
API clients and router
Standardized routes
contracts/components/*.js
Component business contracts
Foundation/Hero/Room/Action data
Allowed fields/default semantics
contracts/schemas/registry-schema.json
Component Registry contract
Manifest/registry definition
Valid registry
contracts/runtime/runtime-contract.json
Live document contract
Publish output
Runtime-compatible payload
contracts/runtime/block-schema.json
Basic block/capsule structure
Component instance
Valid block
19. Registry — catalog of capabilities
The Registry answers the question: "Which components does this version of the Studio know how
to edit?" It doesn't render the page and doesn't save data. It registers capsule modules,
their editors, defaults, and contracts. app.js runs
registry/boot.js; loader.js loads modules according to
manifest.js; index.js exposes the ready catalog.
Project / File
Role in the flow
What it receives
What it passes on
registry/manifest.js
Declares available modules
Build version
List of components
registry/loader.js
Dynamically loads modules
Manifest items
Loaded component modules
registry/index.js
Public registry API
Loaded modules
Lookup by type
registry/boot.js
Orchestrates registration on startup
Manifest and loader
Ready registry
engine/component-registry.js
Runtime map of Studio components
Component module
Editor/default/contract lookup
20. Component as a sovereign capsule
Each component in the Studio project has a small, closed lifecycle. defaults.js
defines what a new instance looks like; editor.js builds the Inspector UI;
index.js registers the component. Foundation is a special persistent DNA layer,
while Hero, Room Card, and Action Card represent content capsules.
Project / File
Role in the flow
What it receives
What it passes on
components/foundation/defaults.js
Initial Website DNA
Preset/new document
Foundation state
components/foundation/presets.js
Pre-made visual systems
User selection
Palette/typography/canvas values
components/foundation/controller.js
Special Foundation rules
Inspector changes
Controlled modification of the persistent layer
components/foundation/editor.js
Foundation Inspector
Foundation state
UI controls
components/hero/*
Hero capsule authoring
Hero state
Title, media, and layout changes
components/room_card/*
Room Card authoring
Room Card state
Card with content/images
components/action_card/*
Internal/external navigational capsule
Link/reference selection
CTA or Page link
21. Experience and Blueprint layer
blueprints/blueprint-engine.js, blueprints/party.js,
experiences/experience-engine.js, and
experiences/party_invitation.js represent the start of a higher level of
automation. A component is a building block, a Page is a screen, an Experience is a Website,
and a Blueprint is a recipe that can create a starting set of pages and capsules in a few
steps.
Experience type selection → Blueprint → Website structure creation
→ Home + additional Pages + initial capsules → the user edits immediately
This is exactly where the product message "3 Click Website" becomes real: click 1 — choose an
Experience; click 2 — choose a blueprint/preset; click 3 — create. Behind the three clicks
runs a complete relational, contract, persistence, and runtime system.
22. UI Shell — working environment
When the Store and Registry are ready, shell/ui-shell.js assembles the visual
editor. topbar.js displays the Website/Page context and main actions.
timeline.js shows the order of capsules. inspector-host.js opens
the editor from the Registry for the currently selected component.
Project / File
Role in the flow
What it receives
What it passes on
shell/ui-shell.js
Assembles the editor layout
Store and services
Top Bar/Timeline/Inspector/Canvas
shell/topbar.js
Global actions and context
Experience/Page status
Save/Publish/navigation events
shell/timeline.js
Capsule order and selection
Store capsules
Selected component/reorder
shell/inspector-host.js
Loads the corresponding component editor
Registry lookup + selection
Inspector controls
css/topbar.css
Top Bar visual layer
Topbar DOM
Clear actions
css/workspace.css
Main editor layout
Shell DOM
Workspace
css/variables.css
Studio design tokens
CSS modules
Consistent appearance
css/splash.css
Boot/loading state
Boot phase
Controlled transition to Hub/Editor
23. Simulator — appearance before publishing
services/simulator-bridge.js is the boundary between the Store and the preview
Runtime. When the reducer changes Foundation or a capsule, the bridge sends the new Draft
state to the simulator. preview-runtime.js in the Runtime project receives the
controlled preview protocol and renders the same mental model as the public Runtime, but
from the Draft and inside the Studio.
Inspector change → Store action → reducer → simulator-bridge
→ preview-runtime.js → render-engine → user immediately sees the result
24. Upload — media ownership
services/upload-service.js sends the file along with the tenant_id,
experience_id, and project_id context. The API router selects
assetPipeline.js, which verifies ownership before writing to R2. The Draft
asset remains private; Publish decides what becomes public and under which stable public
path.
Project / File
Role in the flow
What it receives
What it passes on
studio/services/upload-service.js
Client upload orchestrator
File + full context
Asset reference
api/pipelines/assetPipeline.js
Ownership, validation, and R2 operation
Identity + experienceId + projectId + file
Securely written asset
api/storage.js
R2 primitives
Canonical asset key
Persisted object
runtime/engine/asset-manager.js
Live asset management
References from the Live config
Resolved asset for the renderer
runtime/adapters/dom/asset-resolver.js
Converts references to URL
Asset reference and runtime context
Public or preview URL
25. Autosave and manual Save
autosave-controller.js observes relevant Store changes, debounces them, and
calls api-service.js. The Save request must contain experienceId,
projectId, and the Draft payload. savePipeline.js then verifies
ownership, validates the document against the contract, and writes only the Draft state to
D1.
Store changed → autosave-controller.js → api-service.js POST /api/save
→ router.js → savePipeline.js → contract validation → project_documents state=draft
26. Click on Publish — transition to Live
Publish is the boundary of two worlds. Before the click, there is an authoring Draft. After a
successful publish, there is an immutable public representation that the Runtime is allowed
to read. The Studio does not write Live directly; it only sends the intent to the API.
The Top Bar emits a Publish event for the active Experience and Page.
api-service.js sends a tenant-safe request with experienceId
and projectId.
router.js confirms the route, method, and Identity Context.
publishPipeline.js reloads the canonical Draft from D1 — it does not
blindly trust the browser payload.
The pipeline verifies triple ownership and lifecycle state.
contracts/validator.js validates the Draft.
The Publish transformation creates a Runtime-compatible Live document.
The Live state is written to D1 and/or the canonical public R2 config according to the
contract.
Public asset references are resolved for the Runtime delivery plane.
The API returns success only when the public artifact is consistent.
DRAFT (D1 SSOT) → reload → validate → transform → LIVE (D1 + R2 public config)
→ runtime.selection.rs → Live Website
27. Publish pipeline — audit points
publishPipeline.js needs to be audited according to the canonical Experience V2
contract, not blindly rewritten. The most important thing is to prove that every
SELECT/UPDATE/INSERT uses effectiveTenantId + experienceId + projectId where it
is business-necessary.
Strict publishing conditions
Publish must not find a Page solely by project_id without Experience
affiliation.
It must not publish an archived Page or a Page from an archived Experience.
The Default Page and URL mapping must remain in the same Experience.
Live output must pass runtime-contract validation.
The R2 key and public URL must use stable identities, not a mutable title.
28. Runtime — public world without an editor
When the user clicks "View Live" after Publishing, a public URL path opens. The Runtime has
no Selection Session, Inspector, Timeline, Save, or Draft. It only accepts a public GET
flow, resolves the tenant/Experience/Page from the host and path, loads exclusively the Live
config, and renders it.
https://tenant.selection.rs/{experienceSlug}/{pageSlug}
→ runtime-bootstrap.js → runtime context + URL resolver
→ fetch Live config → render-engine.js → DOMAdapter → public Website
Runtime root files
Project / File
Role in the flow
What it receives
What it passes on
runtime/runtime.html
Public shell document
Browser GET
runtime-bootstrap.js and mount point
runtime/runtime-bootstrap.js
Starts the public Runtime
URL/host
Runtime context and render flow
runtime/preview-runtime.js
Preview variant for the Studio simulator
Draft protocol message
Draft render
runtime/manifest.js
Available Runtime components
Build
Renderer registry list
runtime/fetch-contract.js
Mirroring central contracts
contracts source
Local runtime contracts
runtime/_headers
Security/cache of the public runtime
GET request
CSP and delivery behavior
29. Runtime Context and URL semantics
Project / File
Role in the flow
What it receives
What it passes on
services/runtime-context.js
Assembles the tenant/Experience/Page public context
Host + pathname
Canonical runtime context
services/runtime-protocol.js
Defines the preview/live communication contract
Messages/payload
Secure interpretation
system/context.js
Global runtime system context
Resolved identity/config
Context for the engine
utils/url-resolver.js
Resolves internal Website routes
Experience/Page slug/reference
Final URL
services/capsule-menu.js
Optional runtime capsule UI
Component state
Interactive menu where allowed
30. Render Engine — JSON to screen translation
Project / File
Role in the flow
What it receives
What it passes on
engine/browser-engine.js
Browser host for the engine
Runtime context
Started render system
engine/render-engine.js
Main render orchestration
Live document
DOM result
engine/component-registry.js
Maps type to renderer
Manifest/module
Renderer lookup
engine/component-manager.js
Manages component instances
Capsule array
Lifecycle of instances
engine/component-instance.js
A single runtime component
Type + props
Render/behavior object
engine/layout-engine.js
Layout of components
Capsule/layout data
Positioned output
engine/theme-compiler.js
Foundation in CSS/theme tokens
Foundation Live state
Applies the design system
engine/event-bus.js
Runtime events
Interactions
Loosely coupled reactions
engine/behavior-manager.js
Activates behaviors
Behavior definitions
Animations/audio/interactions
engine/asset-manager.js
Delivers assets
References
Resolved media
31. DOM Adapter and Canvas
Project / File
Role in the flow
What it receives
What it passes on
adapters/dom/BaseDOMRenderer.js
Base rules of the renderer
Component state
Standardized DOM output
adapters/dom/DOMAdapter.js
Converts engine operations into browser DOM
Render instructions
Real HTML nodes
adapters/dom/asset-resolver.js
Asset reference → URL
Runtime context
Image/video URL
canvas/canvas-controller.js
Controls the global canvas
Foundation + viewport
Canvas state
canvas/canvas-overlays.js
Overlay/blur/effect layers
Foundation canvas config
Visual layers
styles/foundation.css
Basic public styles
Runtime DOM
Stable baseline
styles/runtime-shell-css.js
Dynamic shell CSS
Theme/context
Runtime styles
32. Runtime component renderers
The Studio component and Runtime component share the same contract, but do not have the same
responsibility. The Studio editor changes data; the Runtime renderer only reads the valid
Live state and displays it. Therefore, each Runtime component has an index.js
for registration and renderer.js for public display.
Project / File
Role in the flow
What it receives
What it passes on
runtime/components/foundation/renderer.js
Applying Website DNA
Foundation Live state
Global appearance
runtime/components/hero/renderer.js
Public Hero
Hero Live state
Title/media/CTA DOM
runtime/components/room_card/renderer.js
Public Room Card
Room Card state
Card and allowed interactions
runtime/components/action_card/renderer.js
Public navigation card
Reference/link state
Internal Page or external link
33. Behavior system — interactions
After the basic DOM render, behavior-manager.js activates behaviors that are
allowed by the contract. FadeInBehavior adds an entrance animation;
AmbientAudioBehavior controls audio. behavior-registry.js
determines which behavior exists, behavior.js provides the base model, and
event-bus connects the events.
34. Click "Live" — experience and background
USER SEES: Publish → Success → View Live → Website
SYSTEM DOES: Identity → Ownership → Draft reload → Contract validation
→ Live transform → D1/R2 write → Public route resolution → Runtime render
35. Archiving and restoring
A Website is not static forever. A Page and Experience can be archived and restored, but the
lifecycle must respect relational conditions. Page archive/restore belongs to the
API/Workspace layer, while capsule delete belongs to the Store. These are two different
types of "deletion".
Page archive checks active Pages within the same experience_id, not at the
level of the entire tenant.
The only active Page of an active Experience must not be archived without a defined
transition.
Experience archive must define what happens to its Pages, default route, and public Live
content.
Restore must re-check slug/UNIQUE conflicts and valid ownership.
36. Security model by layers
Layer
Project / Technology
Role and Boundary of Responsibility
Perimeter
Cloudflare Access
Perimeter confirmation of the person/domain. Who is trying to enter.
Session
Selection Session
Internal authenticated session (HttpOnly JWT).
Identity
D1 Identity
The sole source of business truth (Account, membership, role, status).
Protocol
Router
The single central enforcement point for all API calls.
Operation
Pipeline
Business operation and triple-scope ownership verification.
Storage
Storage
Persistence without business decision-making (D1/R2/KV).
Display
Runtime
Public reading of the Live state without any private permissions.
37. Separation of the four repositories
The separation of the Studio, Runtime, API, Contracts, and Admin worlds is not accidental. It
prevents the editor from deciding who the user is, the Runtime from getting write
capabilities, the Admin from rendering the Website, or the API from knowing what the
Inspector looks like.
Admin is the Control Plane.
API is the Kernel: identity, authorization, ownership, persistence, and
publish.
Studio is the Authoring Plane: edits the Draft and simulates it.
Runtime is the Delivery Plane: reads only Live and renders it.
Contracts are the shared language that keeps all layers compatible.
38. Complete map of a single request
The user changes the Hero capsule title in the Inspector.
components/hero/editor.js emits a Studio event or Store action.
store/reducer.js creates a new state and history records the change.
simulator-bridge.js sends the Draft preview to the Runtime.
preview-runtime.js and render-engine.js immediately display
the new title.
autosave-controller.js calls api-service.js after a debounce.
The biggest change in Experience V2 is not the new experience_id column. The
biggest change is the mental model: Selection now truly owns the Website level. A Page is a
part of a Website, not the entire product. Because of that, identity, ownership, boot,
lifecycle, navigation, publish, and runtime route must all understand the same chain:
Tenant → Experience → Page.
When a user creates a Website in three clicks, that simplicity didn't arise because the
system is simple. It arose because the complexity is distributed into clear layers, each
with a single owner and a single contract. That is the difference between a demonstration
and a platform.
Selection doesn't just build pages. Selection issues a digital space, guides the user
through it, and publishes the entire Website.