openvibers/openvibe.host Commits
Host: public hosting waits for its launch switch (HOST_PUBLIC_HOSTING, off): staff-only projects and an honest front page until then (#47)
Changes
@@@ -18,6 +18,10 @@ TRUST_PROXY=1# (never under openvibe.network / openvibe.live). Development: localhost (http://<site>.localhost:4910)HOST_SITES_DOMAIN=openvibe.host++# Public hosting is owner-gated (plan T20/O34). Off: only OpenVibe staff can create projects and the front page++# says hosting is not open yet. Set to on at launch.++HOST_PUBLIC_HOSTING=off++# SQLite database and content-addressed objects (the production unit sets both under# /var/lib/openvibe-host-api)# PostgreSQL (ADR-035), written by OpenVibe.Host roles/data/add-service.sh host: the runtime role through PgBouncer,@@@ -111,6 +111,10 @@ function load(env = process.env) {projects: {maxPerOwner: int(env.HOST_MAX_PROJECTS_PER_OWNER, 10),},++ // Public hosting is owner-gated (plan T20/O34: the owner's go-ahead, a DMCA designated agent and a Public++ // Suffix List entry come first). Until HOST_PUBLIC_HOSTING=on only OpenVibe staff can create projects, and the++ // front page says hosting is not open yet; everything else about the engine is unchanged.++ publicHosting: String(env.HOST_PUBLIC_HOSTING || 'off').trim().toLowerCase() === 'on',// Per-project quotas (a row in host_quotas overrides them per project; only staff set it).quotas: {@@@ -66,6 +66,9 @@ function createProjects({ store, config, access, blobs, takedowns, log = console}async function create(viewer, input = {}) {++ if (!config.publicHosting && !viewer.staff) {++ throw new ApiError(403, 'host.not_launched', 'OpenVibe.Host is not open for hosting yet: it opens after a launch review, and only OpenVibe staff can create projects until then');++ }const owner = principalOf(viewer);if (!owner || !ids.isSubjectId('user', owner)) {throw new ApiError(403, 'project.owner_must_be_user', 'a project is owned by a person (usr_…): an app or service must name the owner with X-OV-Subject');@@@ -72,10 +72,10 @@ function createDashboard(ctx) {// The signed-out front page is the public page of openvibe.host (sitemap.xml): indexable.// Everything behind sign-in stays noindex and private.if (req.viewer.kind !== 'user') {-- return page(req, res, 200, null, pages.signedOut({ limits: limitsOf(config), limitsUrl: `${config.baseUrl}/limits.json` }), null,++ return page(req, res, 200, null, pages.signedOut({ limits: limitsOf(config), limitsUrl: `${config.baseUrl}/limits.json`, launched: config.publicHosting }), null,{ indexable: !req.query || !Object.keys(req.query).length, styles: [showcase.STYLESHEET] });}-- page(req, res, 200, 'Projects', pages.home({ projects: await projects.listFor(req.viewer), csrf: csrfToken(config, req.viewer) }), notice(req));++ page(req, res, 200, 'Projects', pages.home({ projects: await projects.listFor(req.viewer), csrf: csrfToken(config, req.viewer), canCreate: config.publicHosting || Boolean(req.viewer.staff) }), notice(req));}));router.get('/projects/:id', view(async (req, res) => {@@@ -22,16 +22,24 @@ const limitValue = (n, unit) => (n === 0 ? 'none' : unit === 'bytes' ? bytes(n)/*** The signed-out front page: the public page of openvibe.host, built with openvibe-shared/showcase. The limits table* is limitsOf(config), the same object /limits.json serves, so the page never promises more than Host enforces.++ * Until public hosting is launched (config.publicHosting) the page says so and invites no one to publish.*/--function signedOut({ limits, limitsUrl } = {}) {-- return showcase.hero({++function signedOut({ limits, limitsUrl, launched = false } = {}) {++ return showcase.hero(launched ? {eyebrow: 'OpenVibe.Host · alpha',title: 'Publish a static site.', accent: 'Roll back in one step.',lede: 'Upload a folder or a .tar.gz of HTML, CSS, JavaScript, images and fonts. Every deploy is kept as an immutable, content-addressed artifact; the active one is served at <site>.openvibe.host and on custom domains you verify.',actions: [{ label: 'Sign in with OpenVibe', href: '/auth/login?next=%2F', primary: true }, { label: 'See the limits', href: '#limits' }],note: 'Alpha. Static files only: there is no build step and no server-side code, and nothing you upload is ever executed. Hosted bots, mods and apps are not available.',++ } : {++ eyebrow: 'OpenVibe.Host · not open yet',++ title: 'Static sites, every deploy kept.', accent: 'Opening after review.',++ lede: 'The hosting engine is built and tested: upload a folder or a .tar.gz, every deploy is kept as an immutable, content-addressed artifact, and the active one is served at <site>.openvibe.host and on custom domains you verify. It is not open to the public yet.',++ actions: [{ label: 'What it will do', href: '#what-you-get', primary: true }, { label: 'See the limits', href: '#limits' }],++ note: 'Public hosting opens after a launch review: abuse handling and a designated DMCA agent come first. Until then only OpenVibe staff can create projects.',}) + showcase.features({-- title: 'What you get',++ id: 'what-you-get',++ title: launched ? 'What you get' : 'What it will do',items: [{ icon: 'ov:upload', title: 'Upload from the browser or the API', text: 'A folder or an archive from the dashboard, or POST it to /api/v1. Archives are checked in memory and never extracted.' },{ icon: 'ov:history', title: 'Every deploy kept', text: 'Deploys never change once made. Activate an earlier one to roll back, from the dashboard or the API.' },@@@ -39,7 +47,7 @@ function signedOut({ limits, limitsUrl } = {}) {{ icon: 'ov:account', title: 'Projects for a team', text: 'Owners add maintainers and members per project. Sandbox projects have smaller quotas and stay out of search.' },],}) + showcase.steps({-- title: 'Get a site online',++ title: launched ? 'Get a site online' : 'How it will work',items: [{ title: 'Sign in', text: 'With your OpenVibe account.' },{ title: 'Create a project', text: 'Production, or a sandbox to try things out.' },@@@ -56,12 +64,12 @@ function signedOut({ limits, limitsUrl } = {}) {}) : '');}--function home({ projects, csrf }) {++function home({ projects, csrf, canCreate = true }) {const rows = projects.map((p) => `<tr><td><a href="/projects/${esc(p.id)}">${esc(p.name)}</a></td><td>${esc(p.environment)}</td><td>${esc(p.role || '')}</td><td>${when(p.created_at)}</td></tr>`).join('');return `<h1>Your projects</h1>${projects.length ? `<table><thead><tr><th>Project</th><th>Environment</th><th>Your role</th><th>Created</th></tr></thead><tbody>${rows}</tbody></table>` : '<p>You have no projects yet.</p>'}<h2>New project</h2>--${form('/projects', csrf, `<label>Name <input name="name" required maxlength="80"></label>++${!canCreate ? '<p>OpenVibe.Host is not open for hosting yet. It opens after a launch review (abuse handling and a designated DMCA agent first); until then only OpenVibe staff can create projects.</p>' : form('/projects', csrf, `<label>Name <input name="name" required maxlength="80"></label><label>Environment <select name="environment"><option value="production">production</option><option value="sandbox">sandbox (smaller quotas, noindex, no custom domains)</option></select></label><button type="submit">Create project</button>`)}`;}nnew file mode 100644@@@ -0,0 +1,52 @@++'use strict';++/**++ * Public hosting is owner-gated (plan T20/O34): until HOST_PUBLIC_HOSTING=on, the front page says hosting is not++ * open yet and invites no one to publish, a person cannot create a project (403 host.not_launched), the dashboard++ * shows why instead of the form, and OpenVibe staff can still create projects to test the engine.++ */++const assert = require('assert');++const { boot, check, done, DASHBOARD } = require('./stageb/boot');++const { csrfToken } = require('../server/auth/forms');++++(async () => {++ const t = await boot({ env: { HOST_PUBLIC_HOSTING: 'off' } });++ const alice = t.user('alice');++ const staff = t.user('root', { role: 'admin' });++++ await check('the signed-out front page says hosting is not open yet and invites no one to publish', async () => {++ const r = await t.api('GET', '/');++ assert.strictEqual(r.status, 200);++ assert.match(r.text, /not open yet/);++ assert.match(r.text, /launch review/);++ assert.match(r.text, /How it will work/);++ assert.doesNotMatch(r.text, /Get a site online/);++ assert.doesNotMatch(r.text, /class="sc-btn sc-primary" href="\/auth\/login/, 'no primary sign-in-to-publish call');++ assert.match(r.text, /id="limits"/, 'the limits the engine enforces are still published');++ });++++ await check('a person cannot create a project until launch', async () => {++ const r = await t.api('POST', '/api/v1/projects', { as: alice, json: { name: 'Too early' } });++ assert.strictEqual(r.status, 403);++ assert.strictEqual(r.json().code, 'host.not_launched');++ });++++ await check('the signed-in dashboard explains instead of offering the form, and its form post is refused', async () => {++ const r = await t.api('GET', '/', { session: alice });++ assert.strictEqual(r.status, 200);++ assert.match(r.text, /Your projects/);++ assert.match(r.text, /not open for hosting yet/);++ assert.doesNotMatch(r.text, /<form[^>]+action="\/projects"/);++ const csrf = csrfToken({ formSecret: 'test-form-secret' }, { subject: alice.subject });++ const post = await t.api('POST', '/projects', { session: alice, form: { csrf, name: 'Too early', environment: 'production' }, headers: { origin: `https://${DASHBOARD}` } });++ assert.ok(!/\/projects\/prj_/.test(post.headers.location || ''), `no project page to go to: ${post.status} ${post.headers.location}`);++ const mine = (await t.api('GET', '/api/v1/projects', { as: alice })).json().projects;++ assert.strictEqual(mine.length, 0, 'nothing was created');++ });++++ await check('OpenVibe staff can still create a project to test the engine', async () => {++ const p = await t.project(staff, 'Staff test');++ assert.match(p.id, /^prj_/);++ });++++ done();++})();@@@ -50,6 +50,7 @@ async function boot(opts = {}) {OV_NETWORK_URL: network.url, OV_NETWORK_INTERNAL_URL: network.url,OV_OAUTH_CLIENT_ID: 'host', OV_OAUTH_CLIENT_SECRET: SECRET, COOKIE_SECURE: 'false',HOST_FORM_SECRET: 'test-form-secret', HOST_WORKER: 'off', HOST_MIN_FREE_BYTES: '0',++ HOST_PUBLIC_HOSTING: 'on', // the suites exercise the engine as launched; test/host-launch-switch.test.js covers off...(opts.env || {}),};const configLib = require('../../server/config');
@@ -18,6 +18,10 @@ TRUST_PROXY=1
@@ -18,6 +18,10 @@ TRUST_PROXY=1
# (never under openvibe.network / openvibe.live). Development: localhost (http://<site>.localhost:4910)
# (never under openvibe.network / openvibe.live). Development: localhost (http://<site>.localhost:4910)
HOST_SITES_DOMAIN=openvibe.host
HOST_SITES_DOMAIN=openvibe.host
+# Public hosting is owner-gated (plan T20/O34). Off: only OpenVibe staff can create projects and the front page
+# says hosting is not open yet. Set to on at launch.
+HOST_PUBLIC_HOSTING=off
+
# SQLite database and content-addressed objects (the production unit sets both under
# SQLite database and content-addressed objects (the production unit sets both under
# /var/lib/openvibe-host-api)
# /var/lib/openvibe-host-api)
# PostgreSQL (ADR-035), written by OpenVibe.Host roles/data/add-service.sh host: the runtime role through PgBouncer,
# PostgreSQL (ADR-035), written by OpenVibe.Host roles/data/add-service.sh host: the runtime role through PgBouncer,
@@ -111,6 +111,10 @@ function load(env = process.env) {
@@ -111,6 +111,10 @@ function load(env = process.env) {
projects: {
projects: {
maxPerOwner: int(env.HOST_MAX_PROJECTS_PER_OWNER, 10),
maxPerOwner: int(env.HOST_MAX_PROJECTS_PER_OWNER, 10),
},
},
+ // Public hosting is owner-gated (plan T20/O34: the owner's go-ahead, a DMCA designated agent and a Public
+ // Suffix List entry come first). Until HOST_PUBLIC_HOSTING=on only OpenVibe staff can create projects, and the
+ // front page says hosting is not open yet; everything else about the engine is unchanged.
+ publicHosting: String(env.HOST_PUBLIC_HOSTING || 'off').trim().toLowerCase() === 'on',
// Per-project quotas (a row in host_quotas overrides them per project; only staff set it).
// Per-project quotas (a row in host_quotas overrides them per project; only staff set it).
quotas: {
quotas: {
@@ -66,6 +66,9 @@ function createProjects({ store, config, access, blobs, takedowns, log = console
@@ -66,6 +66,9 @@ function createProjects({ store, config, access, blobs, takedowns, log = console
}
}
async function create(viewer, input = {}) {
async function create(viewer, input = {}) {
+ if (!config.publicHosting && !viewer.staff) {
+ throw new ApiError(403, 'host.not_launched', 'OpenVibe.Host is not open for hosting yet: it opens after a launch review, and only OpenVibe staff can create projects until then');
+ }
const owner = principalOf(viewer);
const owner = principalOf(viewer);
if (!owner || !ids.isSubjectId('user', owner)) {
if (!owner || !ids.isSubjectId('user', owner)) {
throw new ApiError(403, 'project.owner_must_be_user', 'a project is owned by a person (usr_…): an app or service must name the owner with X-OV-Subject');
throw new ApiError(403, 'project.owner_must_be_user', 'a project is owned by a person (usr_…): an app or service must name the owner with X-OV-Subject');
@@ -72,10 +72,10 @@ function createDashboard(ctx) {
@@ -72,10 +72,10 @@ function createDashboard(ctx) {
// The signed-out front page is the public page of openvibe.host (sitemap.xml): indexable.
// The signed-out front page is the public page of openvibe.host (sitemap.xml): indexable.
// Everything behind sign-in stays noindex and private.
// Everything behind sign-in stays noindex and private.
if (req.viewer.kind !== 'user') {
if (req.viewer.kind !== 'user') {
- return page(req, res, 200, null, pages.signedOut({ limits: limitsOf(config), limitsUrl: `${config.baseUrl}/limits.json` }), null,
+ return page(req, res, 200, null, pages.signedOut({ limits: limitsOf(config), limitsUrl: `${config.baseUrl}/limits.json`, launched: config.publicHosting }), null,
{ indexable: !req.query || !Object.keys(req.query).length, styles: [showcase.STYLESHEET] });
{ indexable: !req.query || !Object.keys(req.query).length, styles: [showcase.STYLESHEET] });
}
}
- page(req, res, 200, 'Projects', pages.home({ projects: await projects.listFor(req.viewer), csrf: csrfToken(config, req.viewer) }), notice(req));
+ page(req, res, 200, 'Projects', pages.home({ projects: await projects.listFor(req.viewer), csrf: csrfToken(config, req.viewer), canCreate: config.publicHosting || Boolean(req.viewer.staff) }), notice(req));
}));
}));
router.get('/projects/:id', view(async (req, res) => {
router.get('/projects/:id', view(async (req, res) => {
@@ -22,16 +22,24 @@ const limitValue = (n, unit) => (n === 0 ? 'none' : unit === 'bytes' ? bytes(n)
@@ -22,16 +22,24 @@ const limitValue = (n, unit) => (n === 0 ? 'none' : unit === 'bytes' ? bytes(n)
/**
/**
* The signed-out front page: the public page of openvibe.host, built with openvibe-shared/showcase. The limits table
* The signed-out front page: the public page of openvibe.host, built with openvibe-shared/showcase. The limits table
* is limitsOf(config), the same object /limits.json serves, so the page never promises more than Host enforces.
* is limitsOf(config), the same object /limits.json serves, so the page never promises more than Host enforces.
+ * Until public hosting is launched (config.publicHosting) the page says so and invites no one to publish.
*/
*/
-function signedOut({ limits, limitsUrl } = {}) {
- return showcase.hero({
+function signedOut({ limits, limitsUrl, launched = false } = {}) {
+ return showcase.hero(launched ? {
eyebrow: 'OpenVibe.Host · alpha',
eyebrow: 'OpenVibe.Host · alpha',
title: 'Publish a static site.', accent: 'Roll back in one step.',
title: 'Publish a static site.', accent: 'Roll back in one step.',
lede: 'Upload a folder or a .tar.gz of HTML, CSS, JavaScript, images and fonts. Every deploy is kept as an immutable, content-addressed artifact; the active one is served at <site>.openvibe.host and on custom domains you verify.',
lede: 'Upload a folder or a .tar.gz of HTML, CSS, JavaScript, images and fonts. Every deploy is kept as an immutable, content-addressed artifact; the active one is served at <site>.openvibe.host and on custom domains you verify.',
actions: [{ label: 'Sign in with OpenVibe', href: '/auth/login?next=%2F', primary: true }, { label: 'See the limits', href: '#limits' }],
actions: [{ label: 'Sign in with OpenVibe', href: '/auth/login?next=%2F', primary: true }, { label: 'See the limits', href: '#limits' }],
note: 'Alpha. Static files only: there is no build step and no server-side code, and nothing you upload is ever executed. Hosted bots, mods and apps are not available.',
note: 'Alpha. Static files only: there is no build step and no server-side code, and nothing you upload is ever executed. Hosted bots, mods and apps are not available.',
+ } : {
+ eyebrow: 'OpenVibe.Host · not open yet',
+ title: 'Static sites, every deploy kept.', accent: 'Opening after review.',
+ lede: 'The hosting engine is built and tested: upload a folder or a .tar.gz, every deploy is kept as an immutable, content-addressed artifact, and the active one is served at <site>.openvibe.host and on custom domains you verify. It is not open to the public yet.',
+ actions: [{ label: 'What it will do', href: '#what-you-get', primary: true }, { label: 'See the limits', href: '#limits' }],
+ note: 'Public hosting opens after a launch review: abuse handling and a designated DMCA agent come first. Until then only OpenVibe staff can create projects.',
}) + showcase.features({
}) + showcase.features({
- title: 'What you get',
+ id: 'what-you-get',
+ title: launched ? 'What you get' : 'What it will do',
items: [
items: [
{ icon: 'ov:upload', title: 'Upload from the browser or the API', text: 'A folder or an archive from the dashboard, or POST it to /api/v1. Archives are checked in memory and never extracted.' },
{ icon: 'ov:upload', title: 'Upload from the browser or the API', text: 'A folder or an archive from the dashboard, or POST it to /api/v1. Archives are checked in memory and never extracted.' },
{ icon: 'ov:history', title: 'Every deploy kept', text: 'Deploys never change once made. Activate an earlier one to roll back, from the dashboard or the API.' },
{ icon: 'ov:history', title: 'Every deploy kept', text: 'Deploys never change once made. Activate an earlier one to roll back, from the dashboard or the API.' },
@@ -39,7 +47,7 @@ function signedOut({ limits, limitsUrl } = {}) {
@@ -39,7 +47,7 @@ function signedOut({ limits, limitsUrl } = {}) {
{ icon: 'ov:account', title: 'Projects for a team', text: 'Owners add maintainers and members per project. Sandbox projects have smaller quotas and stay out of search.' },
{ icon: 'ov:account', title: 'Projects for a team', text: 'Owners add maintainers and members per project. Sandbox projects have smaller quotas and stay out of search.' },
],
],
}) + showcase.steps({
}) + showcase.steps({
- title: 'Get a site online',
+ title: launched ? 'Get a site online' : 'How it will work',
items: [
items: [
{ title: 'Sign in', text: 'With your OpenVibe account.' },
{ title: 'Sign in', text: 'With your OpenVibe account.' },
{ title: 'Create a project', text: 'Production, or a sandbox to try things out.' },
{ title: 'Create a project', text: 'Production, or a sandbox to try things out.' },
@@ -56,12 +64,12 @@ function signedOut({ limits, limitsUrl } = {}) {
@@ -56,12 +64,12 @@ function signedOut({ limits, limitsUrl } = {}) {
}) : '');
}) : '');
}
}
-function home({ projects, csrf }) {
+function home({ projects, csrf, canCreate = true }) {
const rows = projects.map((p) => `<tr><td><a href="/projects/${esc(p.id)}">${esc(p.name)}</a></td><td>${esc(p.environment)}</td><td>${esc(p.role || '')}</td><td>${when(p.created_at)}</td></tr>`).join('');
const rows = projects.map((p) => `<tr><td><a href="/projects/${esc(p.id)}">${esc(p.name)}</a></td><td>${esc(p.environment)}</td><td>${esc(p.role || '')}</td><td>${when(p.created_at)}</td></tr>`).join('');
return `<h1>Your projects</h1>
return `<h1>Your projects</h1>
${projects.length ? `<table><thead><tr><th>Project</th><th>Environment</th><th>Your role</th><th>Created</th></tr></thead><tbody>${rows}</tbody></table>` : '<p>You have no projects yet.</p>'}
${projects.length ? `<table><thead><tr><th>Project</th><th>Environment</th><th>Your role</th><th>Created</th></tr></thead><tbody>${rows}</tbody></table>` : '<p>You have no projects yet.</p>'}
<h2>New project</h2>
<h2>New project</h2>
-${form('/projects', csrf, `<label>Name <input name="name" required maxlength="80"></label>
+${!canCreate ? '<p>OpenVibe.Host is not open for hosting yet. It opens after a launch review (abuse handling and a designated DMCA agent first); until then only OpenVibe staff can create projects.</p>' : form('/projects', csrf, `<label>Name <input name="name" required maxlength="80"></label>
<label>Environment <select name="environment"><option value="production">production</option><option value="sandbox">sandbox (smaller quotas, noindex, no custom domains)</option></select></label>
<label>Environment <select name="environment"><option value="production">production</option><option value="sandbox">sandbox (smaller quotas, noindex, no custom domains)</option></select></label>
<button type="submit">Create project</button>`)}`;
<button type="submit">Create project</button>`)}`;
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
+'use strict';
+/**
+ * Public hosting is owner-gated (plan T20/O34): until HOST_PUBLIC_HOSTING=on, the front page says hosting is not
+ * open yet and invites no one to publish, a person cannot create a project (403 host.not_launched), the dashboard
+ * shows why instead of the form, and OpenVibe staff can still create projects to test the engine.
+ */
+const assert = require('assert');
+const { boot, check, done, DASHBOARD } = require('./stageb/boot');
+const { csrfToken } = require('../server/auth/forms');
+
+(async () => {
+ const t = await boot({ env: { HOST_PUBLIC_HOSTING: 'off' } });
+ const alice = t.user('alice');
+ const staff = t.user('root', { role: 'admin' });
+
+ await check('the signed-out front page says hosting is not open yet and invites no one to publish', async () => {
+ const r = await t.api('GET', '/');
+ assert.strictEqual(r.status, 200);
+ assert.match(r.text, /not open yet/);
+ assert.match(r.text, /launch review/);
+ assert.match(r.text, /How it will work/);
+ assert.doesNotMatch(r.text, /Get a site online/);
+ assert.doesNotMatch(r.text, /class="sc-btn sc-primary" href="\/auth\/login/, 'no primary sign-in-to-publish call');
+ assert.match(r.text, /id="limits"/, 'the limits the engine enforces are still published');
+ });
+
+ await check('a person cannot create a project until launch', async () => {
+ const r = await t.api('POST', '/api/v1/projects', { as: alice, json: { name: 'Too early' } });
+ assert.strictEqual(r.status, 403);
+ assert.strictEqual(r.json().code, 'host.not_launched');
+ });
+
+ await check('the signed-in dashboard explains instead of offering the form, and its form post is refused', async () => {
+ const r = await t.api('GET', '/', { session: alice });
+ assert.strictEqual(r.status, 200);
+ assert.match(r.text, /Your projects/);
+ assert.match(r.text, /not open for hosting yet/);
+ assert.doesNotMatch(r.text, /<form[^>]+action="\/projects"/);
+ const csrf = csrfToken({ formSecret: 'test-form-secret' }, { subject: alice.subject });
+ const post = await t.api('POST', '/projects', { session: alice, form: { csrf, name: 'Too early', environment: 'production' }, headers: { origin: `https://${DASHBOARD}` } });
+ assert.ok(!/\/projects\/prj_/.test(post.headers.location || ''), `no project page to go to: ${post.status} ${post.headers.location}`);
+ const mine = (await t.api('GET', '/api/v1/projects', { as: alice })).json().projects;
+ assert.strictEqual(mine.length, 0, 'nothing was created');
+ });
+
+ await check('OpenVibe staff can still create a project to test the engine', async () => {
+ const p = await t.project(staff, 'Staff test');
+ assert.match(p.id, /^prj_/);
+ });
+
+ done();
+})();
@@ -50,6 +50,7 @@ async function boot(opts = {}) {
@@ -50,6 +50,7 @@ async function boot(opts = {}) {
OV_NETWORK_URL: network.url, OV_NETWORK_INTERNAL_URL: network.url,
OV_NETWORK_URL: network.url, OV_NETWORK_INTERNAL_URL: network.url,
OV_OAUTH_CLIENT_ID: 'host', OV_OAUTH_CLIENT_SECRET: SECRET, COOKIE_SECURE: 'false',
OV_OAUTH_CLIENT_ID: 'host', OV_OAUTH_CLIENT_SECRET: SECRET, COOKIE_SECURE: 'false',
HOST_FORM_SECRET: 'test-form-secret', HOST_WORKER: 'off', HOST_MIN_FREE_BYTES: '0',
HOST_FORM_SECRET: 'test-form-secret', HOST_WORKER: 'off', HOST_MIN_FREE_BYTES: '0',
+ HOST_PUBLIC_HOSTING: 'on', // the suites exercise the engine as launched; test/host-launch-switch.test.js covers off
...(opts.env || {}),
...(opts.env || {}),
};
};
const configLib = require('../../server/config');
const configLib = require('../../server/config');