commit a9a464e2a5f5a07052078eb3446587545f31065a
Author: Salka Elmadani <935@inference-x.com>
Date: Wed Feb 25 01:50:55 2026 +0000
Echo-IX v1.0 — AI chat powered by Inference-X. BSL-1.1. Signature 935.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..17e58f9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,9 @@
+Business Source License 1.1
+
+Licensor: Salka Elmadani, Holding SALKA ELMADANI SA (Zug)
+Licensed Work: Echo-IX
+Change Date: 2030-02-12
+Change License: Apache License 2.0
+Additional Use Grant: Revenue < $1M USD = free use.
+
+Full text: https://mariadb.com/bsl11/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7046568
--- /dev/null
+++ b/README.md
@@ -0,0 +1,21 @@
+# Echo-IX
+
+AI chat interface powered by [Inference-X](https://inference-x.com). 228 KB engine. BSL-1.1.
+
+## Run
+
+```bash
+IX_HOST=127.0.0.1 IX_PORT=8081 node server.js
+```
+
+## API
+
+- `POST /api/echo/chat` — Chat completion
+- `POST /api/echo/stream` — SSE streaming
+- `GET /api/echo/models` — Available models
+- `GET /api/echo/hardware` — Backend status
+- `GET /api/health` — Service health
+
+## License
+
+BSL-1.1 -> Apache 2.0 on 2030-02-12. Signature 935.
diff --git a/deploy.sh b/deploy.sh
new file mode 100755
index 0000000..00afa76
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+set -e
+echo "=== Echo-IX Deploy ==="
+pm2 delete echo-ix 2>/dev/null || true
+IX_HOST=${IX_HOST:-127.0.0.1} IX_PORT=${IX_PORT:-8081} pm2 start server.js --name echo-ix
+pm2 save
+echo "Echo-IX running"
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..62e7437
--- /dev/null
+++ b/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "echo-ix",
+ "version": "1.0.0",
+ "description": "Echo - AI chat by Inference-X",
+ "main": "server.js",
+ "scripts": {"start": "node server.js"},
+ "license": "BUSL-1.1",
+ "private": true
+}
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..daacf8e
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,389 @@
+
+
+
+
+
+Echo - Inference-X
+
+
+
+
+
+
+
+
+
+
+
+
Echov8
+
+
+
+
+
+ ● online
+ |
+
+ |
+ - RAM
+ - vCPU
+ - loaded
+
+
+
+
+
◎
+
Echo
+
228 KB inference engine. Your prompt, your hardware, your data.
+
+
+
+
+
+
Running on Montagne (64GB/16vCPU). First response may take 10-20s while the model loads. Streaming enabled.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..2528c51
--- /dev/null
+++ b/server.js
@@ -0,0 +1,29 @@
+var http=require('http'),fs=require('fs'),path=require('path');
+var PORT=parseInt(process.env.PORT||'3090');
+var IX_HOST=process.env.IX_HOST||'127.0.0.1';
+var IX_PORT=parseInt(process.env.IX_PORT||'8081');
+var DEFAULT_MODEL='qwen2.5-3b-instruct-q4_k_m';
+var rates={};
+function rateOk(k,m,w){var n=Date.now();if(!rates[k])rates[k]=[];rates[k]=rates[k].filter(function(t){return n-t=m)return false;rates[k].push(n);return true}
+function ixProxy(p,b,to,cb){var d=JSON.stringify(b);var o={hostname:IX_HOST,port:IX_PORT,path:p,method:'POST',timeout:to,headers:{'Content-Type':'application/json','Content-Length':Buffer.byteLength(d)}};var r=http.request(o,function(res){var c=[];res.on('data',function(x){c.push(x)});res.on('end',function(){try{cb(null,JSON.parse(Buffer.concat(c).toString()))}catch(e){cb(new Error('Parse'))}})});r.on('error',function(e){cb(e)});r.on('timeout',function(){r.destroy();cb(new Error('Timeout'))});r.write(d);r.end()}
+function ixGet(p,cb){http.get({hostname:IX_HOST,port:IX_PORT,path:p,timeout:5000},function(res){var c=[];res.on('data',function(x){c.push(x)});res.on('end',function(){try{cb(null,JSON.parse(Buffer.concat(c).toString()))}catch(e){cb(new Error('Parse'))}})}).on('error',function(e){cb(e)})}
+function json(res,d,s){res.writeHead(s||200,{'Content-Type':'application/json'});res.end(JSON.stringify(d))}
+function readBody(req,cb){var c=[];req.on('data',function(x){c.push(x)});req.on('end',function(){try{cb(JSON.parse(Buffer.concat(c).toString()))}catch(e){cb({})}})}
+var srv=http.createServer(function(req,res){
+ res.setHeader('Access-Control-Allow-Origin','*');
+ res.setHeader('Access-Control-Allow-Methods','GET,POST,OPTIONS');
+ res.setHeader('Access-Control-Allow-Headers','Content-Type,Authorization');
+ if(req.method==='OPTIONS'){res.writeHead(204);res.end();return}
+ res.setHeader('X-Frame-Options','DENY');
+ res.setHeader('X-Content-Type-Options','nosniff');
+ var url=req.url.split('?')[0];
+ if(url==='/api/health')return json(res,{status:'ok',service:'echo-ix',version:'1.0',signature:'935'});
+ if(url==='/api/echo/models'){ixGet('/v1/models',function(e,d){if(!e&&d&&d.data)return json(res,{data:d.data,source:'live'});json(res,{data:[],source:'offline'})});return}
+ if(url==='/api/echo/hardware'){ixGet('/v1/models',function(e,d){if(e)return json(res,{status:'offline',ram_gb:64,cores:16,models:0});json(res,{status:'online',ram_gb:64,cores:16,models:d.data?d.data.length:0,active_model:DEFAULT_MODEL})});return}
+ if(url==='/api/echo/chat'&&req.method==='POST'){var ip=req.socket.remoteAddress;if(!rateOk('e:'+ip,30,6e4))return json(res,{error:'Rate limit'},429);readBody(req,function(b){var msgs=b.messages||[];if(!msgs.length)return json(res,{error:'Messages required'},400);var t0=Date.now();var m=b.model==='auto'||!b.model?DEFAULT_MODEL:b.model;ixProxy('/v1/chat/completions',{model:m,messages:msgs,max_tokens:Math.min(b.max_tokens||512,2048),temperature:b.temperature||0.7},120000,function(e,d){if(e)return json(res,{choices:[{message:{role:'assistant',content:'Backend loading.'},finish_reason:'stop'}],ix:{backend:'offline'}});d.ix={backend:m,latency_ms:Date.now()-t0};json(res,d)})});return}
+ if(url==='/api/echo/stream'&&req.method==='POST'){var ip=req.socket.remoteAddress;if(!rateOk('e:'+ip,30,6e4))return json(res,{error:'Rate limit'},429);readBody(req,function(b){var msgs=b.messages||[];if(!msgs.length)return json(res,{error:'Messages required'},400);res.writeHead(200,{'Content-Type':'text/event-stream','Cache-Control':'no-cache','Connection':'keep-alive','X-Accel-Buffering':'no'});var m=b.model==='auto'||!b.model?DEFAULT_MODEL:b.model;var d=JSON.stringify({model:m,messages:msgs,max_tokens:Math.min(b.max_tokens||512,2048),temperature:b.temperature||0.7,stream:true});var o={hostname:IX_HOST,port:IX_PORT,path:'/v1/chat/completions',method:'POST',timeout:120000,headers:{'Content-Type':'application/json','Content-Length':Buffer.byteLength(d)}};var p=http.request(o,function(pR){pR.on('data',function(c){res.write(c)});pR.on('end',function(){res.end()})});p.on('error',function(){res.end()});p.write(d);p.end();req.on('close',function(){p.destroy()})});return}
+ if(url==='/'||url==='/echo')url='/index.html';
+ var ext=path.extname(url);var mime={'.html':'text/html','.css':'text/css','.js':'application/javascript','.png':'image/png','.svg':'image/svg+xml'};
+ fs.readFile(path.join(__dirname,'public',url),function(e,d){if(e){fs.readFile(path.join(__dirname,'public','index.html'),function(e2,d2){if(e2){res.writeHead(404);res.end('Not found');return}res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});res.end(d2)});return}res.writeHead(200,{'Content-Type':(mime[ext]||'application/octet-stream')+';charset=utf-8'});res.end(d)});
+});
+srv.listen(PORT,function(){console.log('Echo-IX :'+PORT+' -> '+IX_HOST+':'+IX_PORT+' | sig 935')});