openvibers/openvibe.host Commits
Register Compare public service on port 5280 (#378)
Changes
nnew file mode 100644@@@ -0,0 +1,7 @@++# Compare launch++++Compare is a stateless public read service at openvibe.compare, port 5280. Its only local state is a ten-minute cache. No database, account principal or provider credentials are needed. The service/site manifest and compare.comparison@1 are proposed separately in Contracts; the inventory lifecycle override permits launch before that library is released.++++After preparing `/opt/openvibe.compare` from OpenVibers/OpenVibe.Compare and setting PORT=5280, HOST=127.0.0.1 and BASE_URL=https://openvibe.compare with ov-svc-env, run `sudo python3 scripts/register-compare.py`. This checks port collisions and refuses to overwrite a differing existing service. Ship the ready Compare PR through merge-deploy.sh; install its nginx vhost with ov-vhost-install after the domain certificate exists. The vhost replaces the temporary redirect. Verify the home, `/comparisons/ai-models`, `/api/v1/comparisons`, `/sitemap.xml` and the Markdown response.++++Upstreams are public HTTPS reads: Exchange market, Bench chat.arena board and codes.patch runs, Codes harnesses, and Billing public plans. A failed upstream is shown as unavailable, never a fabricated or zero measurement. Billing's public plans endpoint exposes only checkout rate definitions; customer and ledger routes remain restricted.nnew file mode 100644@@@ -0,0 +1,62 @@++#!/usr/bin/env python3++"""Register the stateless Compare service. Run as root; preserves existing inventory entries."""++import json++import os++import pathlib++import tempfile++import sys++++++def entry():++ return {++ '_note': 'Compare v1: stateless, sourced public comparisons; no database or service credentials.',++ 'repo': '/opt/openvibe.compare',++ 'origin': 'https://openvibe.compare',++ 'units': ['openvibe-compare.service'],++ 'unitSources': {'openvibe-compare.service': 'deploy/systemd/openvibe-compare.service'},++ 'installUnits': True,++ 'envFile': '/etc/openvibe/compare.env',++ 'env': {'required': ['PORT', 'HOST', 'BASE_URL']},++ 'port': 5280,++ 'ready': {'url': 'http://127.0.0.1:5280/api/ready', 'timeoutSeconds': 30},++ 'databases': [],++ 'nginx': {'vhost': 'openvibe.compare.conf', 'repoVhost': 'deploy/nginx/openvibe.compare.conf', 'variant': 'http'},++ 'lifecycle': {++ 'liveness': {'endpoint': '/api/health', 'means': 'HTTP process answers'},++ 'shutdown': {'signal': 'SIGTERM', 'deadlineSeconds': 10, 'drains': ['HTTP server closed']},++ 'startupRecovery': {'resumes': [{'kind': 'state', 'what': 'Refetch public sources after ten-minute cache reset'}]},++ 'rollback': {'conditions': ['Ready probe fails'], 'window': 'Any time', 'blockers': {'none': 'Stateless; no migrations'}},++ 'leases': {'none': 'No workers or writes'},++ },++ }++++++def register(target):++ target = pathlib.Path(target)++ data = json.loads(target.read_text())++ if 'compare' in data['services']:++ if data['services']['compare'] != entry():++ raise SystemExit('compare already registered with different settings; refusing overwrite')++ print('compare already registered')++ return++ for name, service in data['services'].items():++ if service.get('port') == 5280 or service.get('drill', {}).get('port') == 5280:++ raise SystemExit(f'Port 5280 already belongs to {name}')++ data['services']['compare'] = entry()++ st = target.stat()++ fd, tmp = tempfile.mkstemp(prefix='.compare-', dir=target.parent)++ try:++ with os.fdopen(fd, 'w') as out:++ json.dump(data, out, indent=2)++ out.write('\n')++ os.chmod(tmp, st.st_mode & 0o777)++ os.chown(tmp, st.st_uid, st.st_gid)++ os.replace(tmp, target)++ finally:++ if os.path.exists(tmp):++ os.unlink(tmp)++ print('compare registered on port 5280; no database')++++++if __name__ == '__main__':++ register(sys.argv[1] if len(sys.argv) > 1 else '/etc/openvibe/host.json')nnew file mode 100644@@@ -0,0 +1,9 @@++'use strict';++const assert=require('node:assert/strict'),fs=require('node:fs'),os=require('node:os'),path=require('node:path'),{spawnSync}=require('node:child_process');++const dir=fs.mkdtempSync(path.join(os.tmpdir(),'compare-inventory-')),file=path.join(dir,'host.json');++try {++ const run=()=>spawnSync('python3',[path.join(__dirname,'../scripts/register-compare.py'),file],{encoding:'utf8'});++ fs.writeFileSync(file,JSON.stringify({services:{existing:{port:5000}}}));assert.equal(run().status,0);let d=JSON.parse(fs.readFileSync(file));assert.equal(d.services.existing.port,5000);assert.equal(d.services.compare.port,5280);assert.deepEqual(d.services.compare.databases,[]);assert.equal(run().status,0);++ fs.writeFileSync(file,JSON.stringify({services:{collision:{port:5280}}}));assert.notEqual(run().status,0);assert(!JSON.parse(fs.readFileSync(file)).services.compare);++ console.log('compare registration: preserves inventory, idempotent, rejects port collision');++} finally {fs.rmSync(dir,{recursive:true,force:true});}
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+# Compare launch
+
+Compare is a stateless public read service at openvibe.compare, port 5280. Its only local state is a ten-minute cache. No database, account principal or provider credentials are needed. The service/site manifest and compare.comparison@1 are proposed separately in Contracts; the inventory lifecycle override permits launch before that library is released.
+
+After preparing `/opt/openvibe.compare` from OpenVibers/OpenVibe.Compare and setting PORT=5280, HOST=127.0.0.1 and BASE_URL=https://openvibe.compare with ov-svc-env, run `sudo python3 scripts/register-compare.py`. This checks port collisions and refuses to overwrite a differing existing service. Ship the ready Compare PR through merge-deploy.sh; install its nginx vhost with ov-vhost-install after the domain certificate exists. The vhost replaces the temporary redirect. Verify the home, `/comparisons/ai-models`, `/api/v1/comparisons`, `/sitemap.xml` and the Markdown response.
+
+Upstreams are public HTTPS reads: Exchange market, Bench chat.arena board and codes.patch runs, Codes harnesses, and Billing public plans. A failed upstream is shown as unavailable, never a fabricated or zero measurement. Billing's public plans endpoint exposes only checkout rate definitions; customer and ledger routes remain restricted.
new file mode 100644
new file mode 100644
@@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+"""Register the stateless Compare service. Run as root; preserves existing inventory entries."""
+import json
+import os
+import pathlib
+import tempfile
+import sys
+
+
+def entry():
+ return {
+ '_note': 'Compare v1: stateless, sourced public comparisons; no database or service credentials.',
+ 'repo': '/opt/openvibe.compare',
+ 'origin': 'https://openvibe.compare',
+ 'units': ['openvibe-compare.service'],
+ 'unitSources': {'openvibe-compare.service': 'deploy/systemd/openvibe-compare.service'},
+ 'installUnits': True,
+ 'envFile': '/etc/openvibe/compare.env',
+ 'env': {'required': ['PORT', 'HOST', 'BASE_URL']},
+ 'port': 5280,
+ 'ready': {'url': 'http://127.0.0.1:5280/api/ready', 'timeoutSeconds': 30},
+ 'databases': [],
+ 'nginx': {'vhost': 'openvibe.compare.conf', 'repoVhost': 'deploy/nginx/openvibe.compare.conf', 'variant': 'http'},
+ 'lifecycle': {
+ 'liveness': {'endpoint': '/api/health', 'means': 'HTTP process answers'},
+ 'shutdown': {'signal': 'SIGTERM', 'deadlineSeconds': 10, 'drains': ['HTTP server closed']},
+ 'startupRecovery': {'resumes': [{'kind': 'state', 'what': 'Refetch public sources after ten-minute cache reset'}]},
+ 'rollback': {'conditions': ['Ready probe fails'], 'window': 'Any time', 'blockers': {'none': 'Stateless; no migrations'}},
+ 'leases': {'none': 'No workers or writes'},
+ },
+ }
+
+
+def register(target):
+ target = pathlib.Path(target)
+ data = json.loads(target.read_text())
+ if 'compare' in data['services']:
+ if data['services']['compare'] != entry():
+ raise SystemExit('compare already registered with different settings; refusing overwrite')
+ print('compare already registered')
+ return
+ for name, service in data['services'].items():
+ if service.get('port') == 5280 or service.get('drill', {}).get('port') == 5280:
+ raise SystemExit(f'Port 5280 already belongs to {name}')
+ data['services']['compare'] = entry()
+ st = target.stat()
+ fd, tmp = tempfile.mkstemp(prefix='.compare-', dir=target.parent)
+ try:
+ with os.fdopen(fd, 'w') as out:
+ json.dump(data, out, indent=2)
+ out.write('\n')
+ os.chmod(tmp, st.st_mode & 0o777)
+ os.chown(tmp, st.st_uid, st.st_gid)
+ os.replace(tmp, target)
+ finally:
+ if os.path.exists(tmp):
+ os.unlink(tmp)
+ print('compare registered on port 5280; no database')
+
+
+if __name__ == '__main__':
+ register(sys.argv[1] if len(sys.argv) > 1 else '/etc/openvibe/host.json')
new file mode 100644
new file mode 100644
@@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
+'use strict';
+const assert=require('node:assert/strict'),fs=require('node:fs'),os=require('node:os'),path=require('node:path'),{spawnSync}=require('node:child_process');
+const dir=fs.mkdtempSync(path.join(os.tmpdir(),'compare-inventory-')),file=path.join(dir,'host.json');
+try {
+ const run=()=>spawnSync('python3',[path.join(__dirname,'../scripts/register-compare.py'),file],{encoding:'utf8'});
+ fs.writeFileSync(file,JSON.stringify({services:{existing:{port:5000}}}));assert.equal(run().status,0);let d=JSON.parse(fs.readFileSync(file));assert.equal(d.services.existing.port,5000);assert.equal(d.services.compare.port,5280);assert.deepEqual(d.services.compare.databases,[]);assert.equal(run().status,0);
+ fs.writeFileSync(file,JSON.stringify({services:{collision:{port:5280}}}));assert.notEqual(run().status,0);assert(!JSON.parse(fs.readFileSync(file)).services.compare);
+ console.log('compare registration: preserves inventory, idempotent, rejects port collision');
+} finally {fs.rmSync(dir,{recursive:true,force:true});}