ADR-053: The Website/Host boundary — OpenVibe.Website authors, OpenVibe.Host serves
Status: Proposed 2026-10-05 (plan track T19), for the owner's review. Builds on ADR-014 (developer projects and tenancy), ADR-006 (Media object, location and namespace model), ADR-036 (Run is an execution authority, not a Host detail) and ADR-048 (one authority per resource; a control operation is a call to the owning authority). Records the boundary T19 needs; the plan lists no Website ADR.
Evidence
- Host is built and is the site, deploy, domain and serving authority today. Its own surface,
OpenVibe.Hostorigin/mainserver/http/api.js:2-21, states it: sites (GET/POST /projects/:id/sites,GET/DELETE /sites/:id,host.site.manage), config (GET/PUT/DELETE /sites/:id/config,host.site.config), source (GET/PUT/DELETE /sites/:id/source,host.site.manage), deploys (GET/POST /sites/:id/deploys,GET /deploys/:id,GET /deploys/:id/log,POST /deploys/:id/activate,POST /sites/:id/rollback,DELETE /deploys/:id,host.deploy.create/host.site.manage), domains (GET/POST /sites/:id/domains,POST /domains/:id/verify,DELETE /domains/:id,host.domain.manage) and staff takedowns (host.site.manage). - The routes, exactly as they read on
origin/main(the header's prose is not the line numbers): sitesGET /projects/:id/sites:123,POST /projects/:id/sites:127,GET /sites/:id:132,DELETE /sites/:id:136; deploysGET /sites/:id/deploys:139,POST /sites/:id/deploys:197,GET /deploys/:id:199,GET /deploys/:id/log:203,POST /deploys/:id/activate:207,POST /sites/:id/rollback:212,DELETE /deploys/:id:217,POST /sites/:id/source/deploys:265; domains:220,:224,:228,:233; config:237,:241,:245; source:253,:257,:261; takedowns:105-:120. The initial read that named:123,:127,:197,:207,:224and:237-:245matchedorigin/mainon re-read; the surface is larger than that list (takedowns, rollback, source,GET /deploys/:id/log), and this ADR uses the full surface. - Host owns the rows.
OpenVibe.Hostorigin/mainmigrations/0001_initial.sqlcreateshost_sites(:43),host_deploys(:58),host_deploy_files(:78),host_blobs(:88),host_activations(:96),host_deploy_logs(:107),host_domains(:116) andhost_takedowns(:139);host_sites.active_deploy_idis the one serving pointer, andmigrations/0003_preview.sql:15-16addshost_sites.preview_deploy_idandpreview_expires_at. A deploy is immutable and content-addressed:host.deploy@1(contracts/host/deploy.v1.json) carriesstate(ready/failed/deleted),source(archive/files/preview/git), its manifest hash and, for a git deploy, the publicrepo_url/ref/commit_shaprovenance. A site ishost.site@1(contracts/host/site.v1.json): one DNS label, the default hostname/URL and the singleactive_deploy_id. - Host owns domain verification and serving.
host.domain@1(contracts/host/domain.v1.json) is a site's default<site>.openvibe.hostor a custom domain "served only once its DNS TXT record is verified (re-checked daily)", with "TLS certificates … issued by an operator". A domain is bound to a site, not to an authoring project. - Host's capabilities and manifest are registered.
manifests/services/host.jsongives Host the capabilitieshost.site.manage,host.deploy.create,host.domain.manage,host.site.config,namespacesOwned: ["host.*"], the domainsopenvibe.host, and the eventshost.deploy.created,host.deploy.activated,host.deploy.failed,host.domain.verified,host.release.published. Its exposure isinternal/publicSite: placeholder(the domain serves the OpenVibe.Sites placeholder page) — Stage B is alpha and not launched (OpenVibe.HostSTATUS.json:"deployed": "loopback only, not launched", andstages.B"running on the host on loopback :4910 … not launched"). - The host-side contracts already exist.
contracts/host/:project,site,deploy,domain,site-configand thesite-manage-request/result,deploy-create-request/result,domain-manage-request/result,site-config-request/resultpairs, all owned byhostincontracts/catalog.json. - Website is a product, not a service, and has no code. The only artifact is
manifests/products/openvibe.website.json(this checkout): domainopenvibe.website, nameOpenVibe.Website,"relationships": { "noRepo": true, … }, launch "tenant hosting on OpenVibe.Host (built, not launched) and the editor". There is noOpenVibe.Websitecheckout under~/OpenVibers, nomanifests/services/website.json, nomanifests/repositories/OpenVibe.Website.json, and **nowebsite.*id** incontracts/catalog.json(grep: zero). Website has no capability, no namespace and no contract. - Website is not OpenVibe.Sites.
OpenVibe.Sitesis a separate, deliberate component:manifests/services/sites.jsonis the "static placeholder generator; not a runtime" (exposurelive, no capabilities, no namespaces), and the plan deletes it in T11 (plan line 98: "Sites (33 domains) | — | Placeholder generator | deleted in T11"). Website is the product the plan puts on Host, not the placeholder page generator. - The plan. Plan line 96: "Actor, Services, Run, Watch, Website, Zone | — | Not created | T13/T14/T17/T18/T19". T12 (line 531): "Website moves out of this track (T19): the builder/editor composes Host + Codes + Actor + Run + Media, so it is built after those exist instead of being rewritten around them later." T19 (line 776) "Product compositions (each complete when started)": "Every product gets its own authority only for genuinely unique domain data; everything else is composed from existing platform services." The composition row (line 783): "Website (openvibe.website) | Host + Codes + Actor + Run + Media". D36 (line 972): "Website on Host (D36, composed in T19)". T12's Stage B finish line (lines 526-529) is Host's: "tenant vhosts, TLS via ACME for custom domains, objects on Media with replication and backup, per-site headers/redirects/SPA fallback, preview deploys, Git deploys, sitemap/robots". The plan lists no Website ADR and gives T19 compositions, not steps.
Decision
- Website owns authoring; Host owns serving. OpenVibe.Website owns the authoring record a person edits and publishes: its projects (the authoring workspaces), pages (the page tree and content), revisions (the source history a build is made from), builds (the artifact and manifest a revision produces), Media asset pointers (references to
media.object@1objects, ADR-006 — pointers, never a second copy of the bytes) and the intended domains (the hostname(s) the authoring project targets, and the DNS instructions shown to its owner). OpenVibe.Host owns the site, the deploy and its activation, domain binding, DNS verification and TLS, and the actual serving of the bytes. - Host is the one site/deploy authority, and it already is. Every create, read, change, activate, roll back, delete, domain and config operation is the Host route named in Evidence:
GET/POST /projects/:id/sites(server/http/api.js:123,:127),POST /sites/:id/deploys(:197),POST /deploys/:id/activate(:207),POST /sites/:id/domains(:224),GET/PUT/DELETE /sites/:id/config(:237/:241/:245) among them. Website calls Host's own API under its own principal and grants; it does not reach into Host's tables. Whether Host also exposes the common control contract (common.resource-control-request@1, ADR-048) and what its OVRN resource names (ovrn:host:<prj>:site/<id>) are is open (below). - Website keeps no duplicate
host_sites/host_deploysrows. It may hold at most a rebuildable read model of what it shows (a site id, an active deploy id), never the row of record, and never a secondactive_deploy_idpointer: the one serving pointer stayshost_sites.active_deploy_id, exposed as[email protected]_deploy_id(contracts/host/site.v1.json). A Website publish means "upload a build, then activate it" through Host; the answer is Host'shost.deploy@1. - A Website build is not a Host deploy. A revision and a build are authoring facts; a deploy is Host's immutable, content-addressed record of an uploaded output. Website's build manifest names the files and hashes it intends to publish; Host re-validates and stores them, and only a Host deploy can be served, activated or rolled back. The same rule applies to the CI path:
POST /sites/:id/source/deploys(:265) always lands as the site's preview (server/http/api.jsnoActivate), and Website approves it throughPOST /deploys/:id/activatelike any other deploy. - Domains stay Host's rows. Website owns the intended hostname and shows its owner the TXT instructions; Host owns the
host_domainsrow,POST /sites/:id/domains, the daily re-check andPOST /domains/:id/verify, the certificate and the serving (host.domain@1). A domain is bound to a Host site (site_id), not to a Website authoring project. Whether Website keeps any domain row of its own, or reads Host'shost.domains, is open (below). - Media bytes stay Media's; Website holds pointers. An authoring asset is a pointer to a
media.object@1object (ADR-006); a deploy's served bytes are Host'shost_blobs/host_deploy_files(or, per T12, objects on Media with replication and backup). Website never becomes a second object store. - Website is not OpenVibe.Sites, and it is not a new serving stack. It composes Host + Codes + Actor + Run + Media (plan line 783); it never mounts a second Host API, a second
host.*namespace or its own tenant vhosts.
Authority boundaries
| Concern | OpenVibe.Website (authoring) | OpenVibe.Host (serving) | |---|---|---| | Project | the authoring workspace (files, settings, collaborators) it owns | host.project@1 (contracts/host/project.v1.json): the Host tenancy keyed by Network prj_ (network_project_id), quota (GET/PUT /projects/:id/quota, :83/:87) and members (:92/:97) | | Pages | the page tree and content the editor reads/writes | none: a page is not a Host row; it becomes files in a deploy | | Revisions | the source history a build is made from (from the editor or a Codes repository) | none: deploys are immutable content-addressed outputs, not revisions | | Builds | the artifact and manifest a revision produces | a deploy is the uploaded output: POST /sites/:id/deploys (:197), or POST /sites/:id/source/deploys (:265, always preview) | | Media assets | pointers to media.object@1 objects (ADR-006) | the deploy's files (host_deploy_files, host_blobs); Host serves what it holds | | Domains | the intended hostname(s) and the DNS instructions shown to the owner | the served domain: host.domain@1, GET/POST /sites/:id/domains (:220/:224), POST /domains/:id/verify (:228), DELETE /domains/:id (:233); TLS by the operator | | Site | no row; it reads Host's site | host.site@1: GET/POST /projects/:id/sites (:123/:127), GET/DELETE /sites/:id (:132/:136); the one serving pointer host_sites.active_deploy_id | | Deploy / activation | asks Host and shows the answer; keeps no deploy row | GET /sites/:id/deploys (:139), GET /deploys/:id (:199), /log (:203), POST /deploys/:id/activate (:207), POST /sites/:id/rollback (:212), DELETE /deploys/:id (:217) | | Site config | none | GET/PUT/DELETE /sites/:id/config (:237/:241/:245): headers, redirects, SPA fallback (host.site.config) | | Git source | the project's code home (Codes, T19) | GET/PUT/DELETE /sites/:id/source (:253/:257/:261): the repo and branch the project's CI deploys from; Host never clones, fetches or builds it | | Takedown / serve | none | staff takedowns (:105-:120) and serving of the active deploy |
Open questions for the owner
- The authoring contracts. Whether Website's projects, pages, revisions and builds are new
website.*contracts (and their shapes), or whether pages/revisions map onto Codes' repository objects, is open. Nothing exists today: nowebsite.*id, no namespace and no service manifest. - The Website project versus the Host project. Whether a Website authoring project is a distinct row that links a Network
prj_to a Hosthost.project@1, or is the Host project itself with authoring data beside it, is open. The plan's rule (T19, line 778) is only that the product keeps its own authority for genuinely unique domain data. - Domains. Whether Website keeps a domain row of its own (the intended hostname and its approval state) or reads Host's
host.domainsdirectly is open; what is fixed is that the binding, verification, certificate and serving are Host's. - The build artifact. Whether a build's output is stored as an authoring artifact (and where), or exists only as the Host deploy it is uploaded into, is open. T12 says Host keeps "objects on Media with replication and backup", so this may be Media, not Website.
- A Website service manifest. Whether Website registers as a service (a
manifests/services/website.jsonwith a capability and namespace) before its authoring contracts, or ships as a product that only calls Host, is open. - Host's control contract and OVRNs. ADR-048 never mentions Host; this ADR records the call as Host's own API under Website's principal and grants. Whether Host also exposes the common control contract (
common.resource-control-request@1, ADR-048), and what its OVRN resource names look like (ovrn:host:<prj>:site/<id>), is open. - The serving zone and TLS. The Website product manifest promises
yourname.openvibe.website(manifests/products/openvibe.website.json), but Host's manifest owns onlyopenvibe.host. Which zone serves Website tenant sites and who issues their TLS certificates is open.
What this ADR does not claim
- No OpenVibe.Website repository exists on
origin/main; there is noOpenVibe.Websitecheckout, no repository manifest and no service manifest. Every product manifest statement above ismanifests/products/openvibe.website.jsonin this checkout,"noRepo": true. - No
website.*contract exists incontracts/catalog.json, nowebsitenamespace and no Website capability; the authoring model in Decision 1 is the boundary being fixed, not built code. - Host's serving side is alpha and not launched:
manifests/services/host.jsonexposure isinternal/publicSite: placeholder, andOpenVibe.HostSTATUS.jsonsays Stage B "not launched". The routes in Evidence are code onorigin/main, not a public service. - No contract schema is added or changed by this ADR; it is prose and cites the existing
host.*contracts.
Out of scope
- Website's own repository, its editor/UI, its service manifest, its contracts and its deploy (T19).
- Codes' repository/file model and the T16 work; Actor's and Run's generation/execution paths (T17/T14); Media's object API (ADR-006/ADR-031) — Website only holds pointers.
- Host's Stage B launch, Public Suffix List submission and its Stage C integration with Run (T12).
- The
host.*contracts and routes themselves: this ADR cites them, it does not redesign them.
Consequences
- Additive and documentation-only: one new ADR and its row in
docs/adr/README.md. No schema, no manifest and no code change, sodocs/ESTATE.mdandmanifests/repositories/are untouched andnode scripts/estate.js --checkstays clean. - Website can be coded once T12 (Host Stage B), T14 (Run) and T17 (Actor) land, against Host's existing routes, without a second site/deploy model.
- Rollback: the ADR is a document; removing it changes no runtime.