{"_id":"@alayrasystems/nexus","_rev":"5-bec0b2d30e1076c8a89a9b782cc24d05","name":"@alayrasystems/nexus","dist-tags":{"latest":"1.6.5"},"versions":{"1.5.1":{"name":"@alayrasystems/nexus","version":"1.5.1","keywords":["ai-gateway","llm-proxy","openai-compatible","anthropic","litellm-alternative","load-balancing","rate-limiting","cost-tracking","self-hosted"],"license":"Apache-2.0","_id":"@alayrasystems/nexus@1.5.1","maintainers":[{"name":"emabbas","email":"kineticide@gmail.com"}],"homepage":"https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus#readme","bugs":{"url":"https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus/issues"},"bin":{"alayra-nexus":"bin/alayra-nexus.js"},"dist":{"shasum":"ca498037a8b2b0dd2bda0f7d34c9cc8b63d0b8ec","tarball":"https://registry.npmjs.org/@alayrasystems/nexus/-/nexus-1.5.1.tgz","fileCount":163,"integrity":"sha512-y6tl8POZB8nU2bTkTHuyGv4tmENfUgiL7FDC+NNuqySI9l57B7G61SkgQIwB2AyX8wHCybZP/FFg5uiUmfx02Q==","signatures":[{"sig":"MEUCIQDZZ2mDF6I0qUF78Pe57/PJC1ngPrxMVXyXNqEeqk3CKgIgDGM+/jraK48olYtBLPPX/2PqVnkgYFKgpFjdHReq8xo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1790231},"main":"dist/server.js","engines":{"node":">=22"},"gitHead":"3935c08e6734ea52201656abe99eadba06e8a8a8","scripts":{"dev":"tsx watch src/server.ts","lint":"eslint .","seed":"tsx scripts/seed/seed.ts","test":"vitest run","build":"node -e \"fs.rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json","start":"node dist/server.js","db:push":"npx prisma db push","migrate":"npx prisma migrate deploy","prepack":"node scripts/npm/prepack.js","lint:fix":"eslint . --fix","smoke:npx":"tsx scripts/smoke/npx.ts","test:hunt":"tsx scripts/test/huntFlakes.ts","typecheck":"tsc --noEmit","test:watch":"vitest","db:generate":"npx prisma generate && npx prisma generate --schema prisma/schema.sqlite.prisma","postinstall":"node scripts/npm/postinstall.js","demo:fixtures":"tsx scripts/demo/buildFixtures.ts","backup:fixture":"tsx scripts/backup/makeFixture.ts","db:sqlite-schema":"tsx scripts/db/sqliteSchema.ts","smoke:standalone":"tsx scripts/smoke/standalone.ts","bench:sqlite-journal":"tsx scripts/bench/sqliteJournal.ts"},"_npmUser":{"name":"emabbas","email":"kineticide@gmail.com"},"overrides":{"brace-expansion":">=5.0.8"},"repository":{"url":"git+https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus.git","type":"git"},"_npmVersion":"10.9.4","description":"AI key pool proxy — route Claude, Gemini, GPT through one OpenAI-compatible endpoint","directories":{},"_nodeVersion":"22.22.0","dependencies":{"zod":"^3.22.4","jose":"^5.10.0","dotenv":"^16.4.5","prisma":"^5.10.2","fastify":"^5.8.5","ioredis":"^5.3.2","js-tiktoken":"^1.0.21","prom-client":"^15.1.3","@fastify/cors":"^11.0.0","@prisma/client":"^5.10.2","@fastify/helmet":"^13.0.0","@fastify/static":"^10.1.2","@fastify/multipart":"^9.4.0","@opentelemetry/api":"^1.9.1","@fastify/rate-limit":"^10.0.0"},"publishConfig":{"//":"Load-bearing now that the package is scoped: a scoped package defaults to restricted, so without this the first publish would either fail or quietly ship a package nobody can install.","access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.1","eslint":"^9.39.4","vitest":"^4.1.10","globals":"^15.15.0","@eslint/js":"^9.39.4","typescript":"^5.3.3","@types/node":"^20.11.24","typescript-eslint":"^8.63.0"},"_npmOperationalInternal":{"tmp":"tmp/nexus_1.5.1_1785404557111_0.3021729303851053","host":"s3://npm-registry-packages-npm-production"}},"1.5.2":{"name":"@alayrasystems/nexus","version":"1.5.2","keywords":["ai-gateway","llm-proxy","openai-compatible","anthropic","litellm-alternative","load-balancing","rate-limiting","cost-tracking","self-hosted"],"license":"Apache-2.0","_id":"@alayrasystems/nexus@1.5.2","maintainers":[{"name":"emabbas","email":"kineticide@gmail.com"}],"homepage":"https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus#readme","bugs":{"url":"https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus/issues"},"bin":{"alayra-nexus":"bin/alayra-nexus.js"},"dist":{"shasum":"42d4bf036455dbbf638366f2eb55c8528c95a458","tarball":"https://registry.npmjs.org/@alayrasystems/nexus/-/nexus-1.5.2.tgz","fileCount":162,"integrity":"sha512-KHmQWnt6ZEuU//a3k59hgzb6H4R+ii9/PatFi7IRZF08wLnBrgFY8WCdbjaARNffXwKLm69TPyxLfsQABq6KOQ==","signatures":[{"sig":"MEQCICf9UHInlPXY9QbiQM8IhL+nSnVRM//Ck0LSA3tpDvNwAiAPY6TExmAYQySrJBl4sbzbGY0L6r49YiBBE0H12ijGBA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alayrasystems%2fnexus@1.5.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1796524},"main":"dist/server.js","engines":{"node":">=22"},"gitHead":"c48b765efee57866f229fc474cf8c5d47e11167d","scripts":{"dev":"tsx watch src/server.ts","lint":"eslint .","seed":"tsx scripts/seed/seed.ts","test":"vitest run","build":"node -e \"fs.rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json","start":"node dist/server.js","db:push":"npx prisma db push","migrate":"npx prisma migrate deploy","prepack":"node scripts/npm/prepack.js","lint:fix":"eslint . --fix","smoke:npx":"tsx scripts/smoke/npx.ts","test:hunt":"tsx scripts/test/huntFlakes.ts","typecheck":"tsc --noEmit","test:watch":"vitest","db:generate":"npx prisma generate && npx prisma generate --schema prisma/schema.sqlite.prisma","postinstall":"node scripts/npm/postinstall.js","demo:fixtures":"tsx scripts/demo/buildFixtures.ts","backup:fixture":"tsx scripts/backup/makeFixture.ts","db:sqlite-schema":"tsx scripts/db/sqliteSchema.ts","smoke:standalone":"tsx scripts/smoke/standalone.ts","bench:sqlite-journal":"tsx scripts/bench/sqliteJournal.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8b011632-30bd-448c-b4df-c52fead78c3e"}},"overrides":{"brace-expansion":">=5.0.8"},"repository":{"url":"git+https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus.git","type":"git"},"_npmVersion":"12.0.2","description":"AI key pool proxy — route Claude, Gemini, GPT through one OpenAI-compatible endpoint","directories":{},"_nodeVersion":"22.23.1","dependencies":{"zod":"^3.22.4","jose":"^5.10.0","dotenv":"^16.4.5","prisma":"^5.10.2","fastify":"^5.8.5","ioredis":"^5.3.2","js-tiktoken":"^1.0.21","prom-client":"^15.1.3","@fastify/cors":"^11.0.0","@prisma/client":"^5.10.2","@fastify/helmet":"^13.0.0","@fastify/static":"^10.1.2","@fastify/multipart":"^9.4.0","@opentelemetry/api":"^1.9.1","@fastify/rate-limit":"^10.0.0"},"publishConfig":{"//":"Load-bearing now that the package is scoped: a scoped package defaults to restricted, so without this the first publish would either fail or quietly ship a package nobody can install.","access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.1","eslint":"^9.39.4","vitest":"^4.1.10","globals":"^15.15.0","@eslint/js":"^9.39.4","typescript":"^5.3.3","@types/node":"^20.11.24","typescript-eslint":"^8.63.0"},"_npmOperationalInternal":{"tmp":"tmp/nexus_1.5.2_1785413716555_0.3970916292990807","host":"s3://npm-registry-packages-npm-production"}},"1.5.3":{"name":"@alayrasystems/nexus","version":"1.5.3","keywords":["ai-gateway","llm-proxy","openai-compatible","anthropic","litellm-alternative","load-balancing","rate-limiting","cost-tracking","self-hosted"],"license":"Apache-2.0","_id":"@alayrasystems/nexus@1.5.3","maintainers":[{"name":"emabbas","email":"kineticide@gmail.com"}],"homepage":"https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus#readme","bugs":{"url":"https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus/issues"},"bin":{"alayra-nexus":"bin/alayra-nexus.js"},"dist":{"shasum":"f9c9403ee70664986c1658a39308a36de4c05a36","tarball":"https://registry.npmjs.org/@alayrasystems/nexus/-/nexus-1.5.3.tgz","fileCount":162,"integrity":"sha512-9s+wIPOtoywqMKoQiXvaCQugVgNPehflILBoU4rqjk4Ude7DF+GvuXJzab/eSTzRLP6v22eoOX4DKnBQ3drHQg==","signatures":[{"sig":"MEQCIBu3Chc5d3CjFqO9+xshH8+nuvHM8illFqAHPgB2RuAMAiAPuneMgp2iUBCJMw/wn5b9Vanyhs9AI/SIO1EyMDsKOg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alayrasystems%2fnexus@1.5.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1811146},"main":"dist/server.js","engines":{"node":">=22"},"gitHead":"e8e0f1cb59f00f514364b28a86c7096ee89d24f0","scripts":{"dev":"tsx watch src/server.ts","lint":"eslint .","seed":"tsx scripts/seed/seed.ts","test":"vitest run","build":"node -e \"fs.rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json","start":"node dist/server.js","db:push":"npx prisma db push","migrate":"npx prisma migrate deploy","prepack":"node scripts/npm/prepack.js","lint:fix":"eslint . --fix","smoke:npx":"tsx scripts/smoke/npx.ts","test:hunt":"tsx scripts/test/huntFlakes.ts","typecheck":"tsc --noEmit","test:watch":"vitest","db:generate":"npx prisma generate && npx prisma generate --schema prisma/schema.sqlite.prisma","postinstall":"node scripts/npm/postinstall.js","demo:fixtures":"tsx scripts/demo/buildFixtures.ts","backup:fixture":"tsx scripts/backup/makeFixture.ts","db:sqlite-schema":"tsx scripts/db/sqliteSchema.ts","smoke:standalone":"tsx scripts/smoke/standalone.ts","bench:sqlite-journal":"tsx scripts/bench/sqliteJournal.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8b011632-30bd-448c-b4df-c52fead78c3e"}},"overrides":{"brace-expansion":">=5.0.8"},"repository":{"url":"git+https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus.git","type":"git"},"_npmVersion":"12.0.2","description":"AI key pool proxy — route Claude, Gemini, GPT through one OpenAI-compatible endpoint","directories":{},"_nodeVersion":"22.23.1","dependencies":{"zod":"^3.22.4","jose":"^5.10.0","dotenv":"^16.4.5","prisma":"^5.10.2","fastify":"^5.8.5","ioredis":"^5.3.2","js-tiktoken":"^1.0.21","prom-client":"^15.1.3","@fastify/cors":"^11.0.0","@prisma/client":"^5.10.2","@fastify/helmet":"^13.0.0","@fastify/static":"^10.1.2","@fastify/multipart":"^9.4.0","@opentelemetry/api":"^1.9.1","@fastify/rate-limit":"^11.2.0"},"publishConfig":{"//":"Load-bearing now that the package is scoped: a scoped package defaults to restricted, so without this the first publish would either fail or quietly ship a package nobody can install.","access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.1","eslint":"^10.8.0","vitest":"^4.1.10","globals":"^17.8.0","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^22.20.0","typescript-eslint":"^8.63.0"},"_npmOperationalInternal":{"tmp":"tmp/nexus_1.5.3_1785577460912_0.8535239881179115","host":"s3://npm-registry-packages-npm-production"}},"1.6.0":{"name":"@alayrasystems/nexus","version":"1.6.0","keywords":["ai-gateway","llm-proxy","openai-compatible","anthropic","litellm-alternative","load-balancing","rate-limiting","cost-tracking","self-hosted"],"license":"Apache-2.0","_id":"@alayrasystems/nexus@1.6.0","maintainers":[{"name":"emabbas","email":"kineticide@gmail.com"}],"homepage":"https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus#readme","bugs":{"url":"https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus/issues"},"bin":{"alayra-nexus":"bin/alayra-nexus.js"},"dist":{"shasum":"17d621818b218e4e61c8d3b0f8c2e5f84e5d3d9c","tarball":"https://registry.npmjs.org/@alayrasystems/nexus/-/nexus-1.6.0.tgz","fileCount":188,"integrity":"sha512-MrebdCLh1Iptt+A9+UOMFmIgvFoRGt+5U1/maaHnXFtLhYbbDTSpTw1jHN7Xw6SKcflxln6imuYAm5xwl361vg==","signatures":[{"sig":"MEQCIA0yFyGlNRBh7PlwfPrtcelb208PufdqX3heCGKFhhaHAiBvxWvAz25lRveDMNiwiGIfKE3tYZh/gnsZJhMVK0pI4g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alayrasystems%2fnexus@1.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":2167771},"main":"dist/server.js","engines":{"node":">=22"},"gitHead":"88e51a1f45224cf91568ebe475df0af09bf1a92a","scripts":{"dev":"tsx watch src/server.ts","lint":"eslint .","seed":"tsx scripts/seed/seed.ts","test":"vitest run","build":"node -e \"fs.rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json","start":"node dist/server.js","db:push":"npx prisma db push","migrate":"npx prisma migrate deploy","prepack":"node scripts/npm/prepack.js","bench:k6":"tsx scripts/bench/k6.ts","db:drift":"prisma migrate diff --from-migrations prisma/migrations --to-schema prisma/schema.prisma --script","lint:fix":"eslint . --fix","smoke:npx":"tsx scripts/smoke/npx.ts","test:hunt":"tsx scripts/test/huntFlakes.ts","typecheck":"tsc --noEmit && tsc -p tsconfig.scripts.json","test:watch":"vitest","bench:cache":"tsx scripts/bench/cache.ts","bench:guard":"tsx scripts/bench/guard.ts","db:generate":"npx prisma generate && npx prisma generate --schema prisma/schema.sqlite.prisma","postinstall":"node scripts/npm/postinstall.js","bench:profile":"tsx scripts/bench/profile.ts","bench:queries":"tsx scripts/bench/queryCount.ts","bench:routing":"tsx scripts/bench/routing.ts","bench:scaling":"tsx scripts/bench/scaling.ts","demo:fixtures":"tsx scripts/demo/buildFixtures.ts","backup:fixture":"tsx scripts/backup/makeFixture.ts","bench:failures":"tsx scripts/bench/failure.ts","bench:overhead":"tsx scripts/bench/overhead.ts","bench:provision":"tsx scripts/bench/provision.ts","bench:resources":"tsx scripts/bench/resources.ts","bench:store-ops":"tsx scripts/bench/storeOps.ts","db:column-facts":"tsx scripts/db/columnFacts.ts","db:sqlite-schema":"tsx scripts/db/sqliteSchema.ts","smoke:standalone":"tsx scripts/smoke/standalone.ts","bench:sqlite-journal":"tsx scripts/bench/sqliteJournal.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8b011632-30bd-448c-b4df-c52fead78c3e"}},"overrides":{"brace-expansion":">=5.0.9"},"repository":{"url":"git+https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus.git","type":"git"},"_npmVersion":"12.0.2","description":"AI key pool proxy — route Claude, Gemini, GPT through one OpenAI-compatible endpoint","directories":{},"_nodeVersion":"22.23.1","dependencies":{"zod":"^3.22.4","jose":"^5.10.0","dotenv":"^16.4.5","prisma":"^7.9.1","fastify":"^5.8.5","ioredis":"^5.3.2","js-tiktoken":"^1.0.21","prom-client":"^15.1.3","@fastify/cors":"^11.0.0","@prisma/client":"^7.9.1","@fastify/helmet":"^13.0.0","@fastify/static":"^10.1.2","@fastify/multipart":"^9.4.0","@opentelemetry/api":"^1.9.1","@prisma/adapter-pg":"^7.9.1","@fastify/rate-limit":"^11.2.0","@prisma/adapter-better-sqlite3":"^7.9.1"},"publishConfig":{"//":"Load-bearing now that the package is scoped: a scoped package defaults to restricted, so without this the first publish would either fail or quietly ship a package nobody can install.","access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.1","eslint":"^10.8.0","vitest":"^4.1.10","globals":"^17.8.0","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^22.20.0","typescript-eslint":"^8.63.0"},"_npmOperationalInternal":{"tmp":"tmp/nexus_1.6.0_1786281291750_0.03705197921555903","host":"s3://npm-registry-packages-npm-production"}},"1.6.5":{"_id":"@alayrasystems/nexus@1.6.5","bin":{"alayra-nexus":"bin/alayra-nexus.js"},"bugs":{"url":"https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus/issues"},"dist":{"shasum":"bf1c3cbdd9d4261f414e8973506761b364e751de","tarball":"https://registry.npmjs.org/@alayrasystems/nexus/-/nexus-1.6.5.tgz","fileCount":199,"integrity":"sha512-pnXoqvV1SvweYXJOaIn/2Fcg+a489jbk86r8tJN3yejeTe1OlZb2a1uTwDyhuWs38kpY9JbDL7d9dvGEh7QBKA==","signatures":[{"sig":"MEUCIQCEW5/KfD+uI1ZlBhjaLCf4u84v8sJ1Wl6jsyUkqK45YgIgLJnkC89FjM+o0hzMRJkRc0NfMtBgju5tEtx6ySjO/NA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDLPCFoADgtPBQu02yeALBivSR9D4HPLwLqllT30yfZhgIgWAaRj+/PGHFvI3eaNWW3tZveUV7UsPhVKH0xZyrHDPU="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alayrasystems%2fnexus@1.6.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":2274917},"main":"dist/server.js","name":"@alayrasystems/nexus","engines":{"//":"22.12 rather than 22, because `jose` v6 is an ESM-only package and dist/ is CommonJS. It loads only through Node's require(esm), which is on by default from 22.12.0; measured on 22.11-equivalent (`--no-experimental-require-module`) it is ERR_REQUIRE_ESM at boot, before a single route is registered.","node":">=22.12.0"},"gitHead":"6edef2a32da68c40c912743dac2ae731851ba79b","license":"Apache-2.0","scripts":{"dev":"tsx watch src/server.ts","lint":"eslint .","seed":"tsx scripts/seed/seed.ts","test":"vitest run","build":"node -e \"fs.rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json","start":"node dist/server.js","db:push":"npx prisma db push","migrate":"npx prisma migrate deploy","prepack":"node scripts/npm/prepack.js","bench:k6":"tsx scripts/bench/k6.ts","db:drift":"prisma migrate diff --from-migrations prisma/migrations --to-schema prisma/schema.prisma --script","gate:e2e":"tsx scripts/release-gate.ts","lint:fix":"eslint . --fix","smoke:npx":"tsx scripts/smoke/npx.ts","test:hunt":"tsx scripts/test/huntFlakes.ts","typecheck":"tsc --noEmit && tsc -p tsconfig.scripts.json","test:watch":"vitest","bench:cache":"tsx scripts/bench/cache.ts","bench:guard":"tsx scripts/bench/guard.ts","db:generate":"npx prisma generate && npx prisma generate --schema prisma/schema.sqlite.prisma","postinstall":"node scripts/npm/postinstall.js","bench:profile":"tsx scripts/bench/profile.ts","bench:queries":"tsx scripts/bench/queryCount.ts","bench:routing":"tsx scripts/bench/routing.ts","bench:scaling":"tsx scripts/bench/scaling.ts","demo:fixtures":"tsx scripts/demo/buildFixtures.ts","backup:fixture":"tsx scripts/backup/makeFixture.ts","bench:failures":"tsx scripts/bench/failure.ts","bench:overhead":"tsx scripts/bench/overhead.ts","docs:providers":"tsx scripts/docs/providerTable.ts","bench:provision":"tsx scripts/bench/provision.ts","bench:resources":"tsx scripts/bench/resources.ts","bench:store-ops":"tsx scripts/bench/storeOps.ts","db:column-facts":"tsx scripts/db/columnFacts.ts","db:sqlite-schema":"tsx scripts/db/sqliteSchema.ts","smoke:standalone":"tsx scripts/smoke/standalone.ts","verify:providers":"tsx scripts/verify-providers.ts","test:redis-outage":"tsx scripts/test/redisOutage.ts","bench:multi-instance":"tsx scripts/bench/multiInstance.ts","bench:sqlite-journal":"tsx scripts/bench/sqliteJournal.ts"},"version":"1.6.5","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8b011632-30bd-448c-b4df-c52fead78c3e"}},"homepage":"https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus#readme","keywords":["ai-gateway","llm-proxy","openai-compatible","anthropic","litellm-alternative","load-balancing","rate-limiting","cost-tracking","self-hosted"],"overrides":{"mysql2":"^3.24.4","deepmerge-ts":"^8.0.2","brace-expansion":">=5.0.9"},"repository":{"url":"git+https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus.git","type":"git"},"_npmVersion":"12.0.2","description":"AI key pool proxy — route Claude, Gemini, GPT through one OpenAI-compatible endpoint","directories":{},"maintainers":[{"name":"emabbas","email":"kineticide@gmail.com"}],"_nodeVersion":"22.23.2","dependencies":{"zod":"^4.4.3","jose":"^6.2.8","dotenv":"^17.4.2","prisma":"^7.9.1","fastify":"^5.12.5","ioredis":"^6.0.0","js-tiktoken":"^1.0.21","prom-client":"^15.1.3","@fastify/cors":"^11.0.0","@prisma/client":"^7.9.1","@fastify/helmet":"^13.0.0","@fastify/static":"^10.1.2","@fastify/multipart":"^10.1.0","@opentelemetry/api":"^1.9.1","@prisma/adapter-pg":"^7.9.1","@fastify/rate-limit":"^11.2.0","@prisma/adapter-better-sqlite3":"^7.9.1"},"publishConfig":{"//":"Load-bearing now that the package is scoped: a scoped package defaults to restricted, so without this the first publish would either fail or quietly ship a package nobody can install.","access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.7.1","eslint":"^10.8.0","vitest":"^5.0.1","globals":"^17.8.0","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^22.20.0","typescript-eslint":"^8.63.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/nexus_1.6.5_1789891592710_0.708943300279401"}}},"time":{"created":"2026-07-30T09:42:36.989Z","modified":"2026-09-20T08:06:33.193Z","1.5.1":"2026-07-30T09:42:37.278Z","1.5.2":"2026-07-30T12:15:16.749Z","1.5.3":"2026-08-01T09:44:21.106Z","1.6.0":"2026-08-09T13:14:51.947Z","1.6.5":"2026-09-20T08:06:32.845Z"},"bugs":{"url":"https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus/issues"},"license":"Apache-2.0","homepage":"https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus#readme","keywords":["ai-gateway","llm-proxy","openai-compatible","anthropic","litellm-alternative","load-balancing","rate-limiting","cost-tracking","self-hosted"],"repository":{"url":"git+https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus.git","type":"git"},"description":"AI key pool proxy — route Claude, Gemini, GPT through one OpenAI-compatible endpoint","maintainers":[{"name":"emabbas","email":"kineticide@gmail.com"}],"readme":"<div align=\"center\">\n\n<br>\n\n<img src=\"./brand/png/alayra-nexus-banner-readme.png\" alt=\"Alayra Nexus — The Enterprise AI Gateway\" width=\"100%\"/>\n\n<br>\n\n**One OpenAI-compatible endpoint. Every model. Zero key chaos.**\n\n<br>\n\n[![CI](https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus/actions/workflows/ci.yml/badge.svg)](https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus/actions/workflows/ci.yml)\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-6d28d9.svg?style=for-the-badge)](./LICENSE)\n[![Release](https://img.shields.io/github/v/release/Alayra-Systems-Pvt-Limited/Alayra-Nexus?style=for-the-badge&color=0e7490)](https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus/releases)\n[![npm](https://img.shields.io/npm/v/%40alayrasystems%2Fnexus?style=for-the-badge&logo=npm&logoColor=white&color=cb0000)](https://www.npmjs.com/package/@alayrasystems/nexus)\n[![npm downloads](https://img.shields.io/npm/dm/%40alayrasystems%2Fnexus?style=for-the-badge&logo=npm&logoColor=white&color=cb0000&label=npm%20installs)](https://www.npmjs.com/package/@alayrasystems/nexus)\n[![Docker Pulls](https://img.shields.io/docker/pulls/alayrasystems/nexus?style=for-the-badge&logo=docker&logoColor=white&color=2496ed)](https://hub.docker.com/r/alayrasystems/nexus)\n[![Container](https://img.shields.io/badge/ghcr.io-alayra--nexus-2496ed.svg?style=for-the-badge&logo=docker&logoColor=white)](https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus/pkgs/container/alayra-nexus)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.3-3b82f6.svg?style=for-the-badge&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)\n[![Fastify](https://img.shields.io/badge/Fastify-v5-22c55e.svg?style=for-the-badge)](https://fastify.dev/)\n[![Node.js](https://img.shields.io/badge/Node.js-22.12+-f59e0b.svg?style=for-the-badge&logo=nodedotjs&logoColor=white)](https://nodejs.org/)\n[![PostgreSQL](https://img.shields.io/badge/PostgreSQL-Prisma-0ea5e9.svg?style=for-the-badge&logo=postgresql&logoColor=white)](https://prisma.io/)\n[![CLI](https://img.shields.io/badge/CLI-coming_soon-64748b.svg?style=for-the-badge&logo=gnubash&logoColor=white)](#contents)\n\n<br>\n\nRoute **Anthropic**, **OpenAI**, **Google Gemini**, **Groq**, **OpenRouter**, **Mistral**,\n**HuggingFace**, **Cloudflare Workers AI** — or anything OpenAI-compatible —  \nthrough a single hardened proxy. Pool multiple API keys per provider, load-balance  \nacross them, auto-failover between tiers, and give every team their own scoped key —  \nwith full usage analytics and cost tracking built in.\n\n<br>\n\n<a href=\"https://alayra-systems-pvt-limited.github.io/Alayra-Nexus/demo/\" target=\"_blank\" rel=\"noopener\"><img src=\"https://img.shields.io/badge/▶%20Live%20demo-open%20the%20dashboard-6d28d9?style=for-the-badge\" alt=\"Open the live demo\"/></a>\n\n**[Quick start](#quick-start)** · [How it compares](#how-it-compares) · [Screens](#screens) · [Connect your tools](#connect-your-tools) · [API](#api-reference)\n\n<sub>The demo is the real dashboard, signed in as a viewer, reading a frozen snapshot of a gateway\nseeded with synthetic traffic. No sign-up, nothing to install, and nothing you do there is saved.</sub>\n\n**Running, from nothing:**\n\n```bash\ncurl -O https://raw.githubusercontent.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus/main/docker-compose.yml\nprintf 'MASTER_ENCRYPTION_KEY=%s\\nADMIN_PASSWORD=change-me\\n' \"$(openssl rand -hex 32)\" > .env\ndocker compose up -d\n```\n\nDashboard on **localhost:3000** · OpenAI-compatible API on **localhost:3000/v1**\n\n<sub>Three lines, not one: the gateway will not start without an encryption key, and generating\none for you silently would mean every install shared a key we published.</sub>\n\n<br>\n\n> Built and maintained by **[Alayra Systems Pvt. Limited](https://github.com/Alayra-Systems-Pvt-Limited)** · Islamabad, Pakistan\n\n<br>\n\n<img src=\"./docs/assets/dashboard-overview.png\" alt=\"The Alayra Nexus overview — request volume, token and cost trends, top models, and the audit trail\" width=\"100%\"/>\n\n<sub>The overview after 90 days of traffic across five teams and ten models.</sub>\n\n<br>\n\n</div>\n\n---\n\n## Contents\n\n**Get started** · [Live demo](https://alayra-systems-pvt-limited.github.io/Alayra-Nexus/demo/) · [Why Alayra Nexus?](#why-alayra-nexus) · [Features](#features) · [Screens](#screens) · [How it compares](#how-it-compares) · [Supported providers](#supported-providers) · [Architecture](#architecture) · [Quick start](#quick-start) · [Standalone mode](#standalone-mode--no-postgres-no-redis) · [Backup & restore](#backup--restore) · [Connect your tools](#connect-your-tools) · [Environment variables](#environment-variables)\n\n**How it works** · [Rate limits, explained](#rate-limits-explained) · [Resilience & routing](#resilience--routing) · [Teams & budgets](#teams--budgets) · [BYOK](#byok--bring-your-own-key) · [API reference](#api-reference) · [Dashboard](#dashboard) · [Observability](#observability)\n\n**Operate & contribute** · [Security model](#security-model) · [Accounts and roles](#accounts-and-roles) · [Roadmap](#roadmap) · [Contributing](#contributing) · [License](#license)\n\n> [!NOTE]\n> **A command-line interface is coming soon** — everything the dashboard does is\n> already an HTTP API today, so the CLI is a convenience layer over endpoints that\n> exist, not new capability. Until it lands, the [admin API](#api-reference) and the\n> web dashboard cover every operation.\n\n---\n\n## Why Alayra Nexus?\n\nMost teams hit the same wall: multiple AI providers, API keys scattered across engineers, no visibility into who spent what, and a hard-coded provider string that makes switching models painful.\n\nAlayra Nexus is the infrastructure layer that sits between your application and every AI provider. Change **one URL**. Get load balancing, automatic failover, team-level access control, and a live cost dashboard — without touching your application code.\n\n---\n\n## Features\n\n| Capability | Details |\n|---|---|\n| **Key Pool Management** | Store unlimited API keys per provider, encrypted at rest with AES-256-GCM |\n| **Intelligent Load Balancing** | Automatic rotation across active keys; cooling and banned keys are automatically bypassed |\n| **Circuit Breaker** | Per-key breaker with escalating cooldown, a single half-open recovery probe, separate 429 handling, and auto-ban on repeated auth failures |\n| **Cache-Aware Sticky Routing** | Multi-turn conversations stay pinned to the same upstream so the provider's prompt cache isn't thrown away by round-robin |\n| **Content Guardrails** | Optional, pluggable prompt/response filtering — redact PII or block banned content and injection patterns. Off by default |\n| **Tiered Failover** | Premium → Standard → Fast chains; when the best key fails the next tier fires instantly |\n| **Cost-Aware Routing** | Optional: within a tier, bias toward the cheapest healthy, in-headroom provider using registry pricing — a tiebreaker that never overrides health or cache affinity |\n| **OpenAI-Compatible API** | Drop-in `/v1/chat/completions` — change one base URL, nothing else |\n| **Anthropic-Compatible API** | `/v1/messages` too, so **Claude Code** and the Anthropic SDKs route through the same pool — streaming, tools, and all |\n| **Team Key Issuance** | Create scoped access tokens per team, each with an independently configurable RPM limit |\n| **BYOK (Bring Your Own Key)** | A team can register its own provider keys, encrypted at rest and routed only for that team's traffic — with optional fall-back to the shared pool, or hard isolation |\n| **Real-Time Rate Limiting** | Per-key RPM **and TPM** enforced atomically before admission, with token reservation and post-response reconciliation, plus live utilization meters |\n| **Cost Tracking** | Per-request USD cost computed from model pricing, attributed to the requesting team |\n| **Full Analytics Dashboard** | Request trends, token breakdowns, team leaderboard, provider split — powered by Chart.js |\n| **Custom Date Ranges** | Analytics filterable by today / 7d / 30d / 90d or any custom from→to window |\n| **CSV Export** | One-click export of all analytics data for finance or reporting |\n| **Model Registry** | Manage which models are available, their tier, capabilities, and per-1M token pricing |\n| **Encrypted Backup & Restore** | One encrypted file for the whole gateway. Secrets are re-keyed on the way in, so a backup restores onto a *different* gateway with a different master key — PostgreSQL ⇄ SQLite included. Every restore is dry-run first |\n| **Standalone Mode** | No Postgres, no Redis — a SQLite file and in-process memory. One process, one directory, nothing to provision |\n| **Web Admin Dashboard** | Full browser UI — no CLI required for day-to-day operations |\n| **Two-Factor Admin Auth** | Optional TOTP second factor with single-use recovery codes, session tokens, per-source login lockout, and revocable API tokens for scripts |\n| **Security Hardened** | Fastify Helmet, CORS, constant-time secret comparison, AES-256-GCM key encryption, zero plaintext secrets at rest |\n\n---\n\n## Screens\n\n<table>\n<tr>\n<td width=\"50%\">\n\n<img src=\"./docs/assets/dashboard-analytics.png\" alt=\"Analytics — success rate, latency percentiles, spend, cache savings, and a per-provider breakdown\"/>\n\n**Analytics** — reliability, speed, spend and savings over any window, with a\nper-model and per-provider breakdown and a plain-English account of what failed.\n\n</td>\n<td width=\"50%\">\n\n<img src=\"./docs/assets/dashboard-teams.png\" alt=\"Team detail — budget consumption, per-key spend share, and busiest models\"/>\n\n**Teams** — each team's budget against its cap, what every access key inside it\nspent, and the models it leans on.\n\n</td>\n</tr>\n<tr>\n<td width=\"50%\">\n\n<img src=\"./docs/assets/dashboard-nexus.png\" alt=\"Provider pools grouped by routing tier, each key showing its limits and status\"/>\n\n**Pools & routing** — provider pools by tier, every key with its own limits,\nstatus and controls: test, cool, ban.\n\n</td>\n<td width=\"50%\">\n\n<img src=\"./docs/assets/dashboard-logs.png\" alt=\"The audit trail — every state-changing action with actor, role, target, result and source IP\"/>\n\n**Audit trail** — every state-changing action with who did it, from where, and\nwhat the server answered. Read-only over the API.\n\n</td>\n</tr>\n<tr>\n<td width=\"50%\">\n\n<img src=\"./docs/assets/dashboard-add-provider.png\" alt=\"The add-provider dialog, covering base URL, auth header, model id path and extra headers\"/>\n\n**Any OpenAI-compatible endpoint** — base URL, auth header, model-id path and\nper-provider headers, without touching a config file.\n\n</td>\n<td width=\"50%\">\n\n<img src=\"./docs/assets/health.png\" alt=\"The health page — live probes for Redis, Postgres and provider reachability\"/>\n\n**Health** — live probes for every dependency, so an outage names itself\ninstead of arriving as a wall of failed requests.\n\n</td>\n</tr>\n</table>\n\n<div align=\"center\">\n<img src=\"./docs/assets/dashboard-mobile.png\" alt=\"The dashboard on a phone, with the navigation drawer open\" width=\"300\"/>\n\n<sub>The console works on a phone — the sidebar becomes a drawer below 820px.</sub>\n</div>\n\n---\n\n## How it compares\n\n[LiteLLM](https://github.com/BerriAI/litellm) is the closest well-known project, so\nhere is an honest side-by-side. It wins on reach and ecosystem; Nexus wins on what\nyou get without paying, and on depth of the operator console.\n\n| | Alayra Nexus | LiteLLM |\n|---|---|---|\n| **Providers** | 5 first-class + any OpenAI-compatible endpoint | **100+ built in** |\n| **Admin dashboard** | Built in — analytics, teams, pools, audit, health | Built in |\n| **Scoped team keys** | Yes | Yes (virtual keys) |\n| **Team budgets** | Yes, with block / notify / downgrade at the cap | Yes |\n| **Key pooling & failover** | Per-provider pools, tiered failover, circuit breaker | Load balancing, retries, fallbacks |\n| **Audit trail** | Append-only, per-action, no delete endpoint | Logging integrations |\n| **SSO** | **Included, Apache-2.0** | Commercial licence |\n| **Two-factor auth** | **Included** | — |\n| **CLI** | *Coming soon* | **Mature** |\n| **SDK** | *Not yet* | **Python SDK** |\n| **Language** | TypeScript / Node | Python (Rust core) |\n| **Licence** | Apache-2.0, every feature included | Open source + commercial tier |\n\n**Choose LiteLLM** if you need breadth of providers today, a mature CLI and SDK, or\nits ecosystem of integrations.\n\n**Choose Alayra Nexus** if you want a self-hosted gateway whose enterprise controls —\nSSO, two-factor, audit trail, team budgets — are in the open-source licence rather\nthan behind a sales call, and a console your finance team can read without help.\n\n<sub>Compared against LiteLLM's public documentation, July 2026. If anything here has\ngone out of date, please open an issue — we would rather fix it than win on a stale fact.</sub>\n\n---\n\n## Supported Providers\n\nNexus ships a preset for each of these — base URL, auth header, model-list endpoint and model-id\npath already filled in. **The list is not a whitelist**: a provider slug is free text, so a pool\npointed at anything OpenAI-compatible is a first-class pool whether or not it appears below. A\npreset only saves you typing.\n\n<!-- BEGIN GENERATED PROVIDER TABLE — npm run docs:providers -->\n\n**6 providers have served a real completion through these presets** — measured 2026-09-20.\n\n| Provider | Verified | Endpoint | Publishes prices? |\n|---|---|---|---|\n| **Groq** | ✅ Completion · 2026-09-20 | `api.groq.com/openai/v1` | ✅ Yes, per model |\n| **OpenRouter** | ✅ Completion · 2026-09-20 | `openrouter.ai/api/v1` | ✅ Yes, per model |\n| **Google** | ✅ Completion · 2026-09-20 | `generativelanguage.googleapis.com/v1beta/openai` | ❌ Set prices yourself |\n| **Mistral** | ✅ Completion · 2026-09-20 | `api.mistral.ai/v1` | ❌ Set prices yourself |\n| **HuggingFace** | ✅ Completion · 2026-09-20 | `router.huggingface.co/v1` | ❌ Set prices yourself |\n| **Cloudflare Workers AI** | ✅ Completion · 2026-09-20 | `api.cloudflare.com/client/v4/accounts/{account_id}/ai/v1` | ❌ Set prices yourself — bills in *neurons*, not tokens |\n| **Cerebras** | ⚠️ Model list only · 2026-09-20 — completions answered 402 until the account is funded | `api.cerebras.ai/v1` | ❌ Set prices yourself |\n| **OpenAI** | ⚪ Preset only | `api.openai.com/v1` | ❌ Set prices yourself |\n| **Anthropic** | ⚪ Preset only | `api.anthropic.com/v1` | ❌ Set prices yourself |\n| **Custom** | ⚪ You configure it | whatever you point it at | Depends on the endpoint |\n| Azure OpenAI · Bedrock · Vertex | ⚪ Via **Custom** | your endpoint | Reachable today through a Custom pool if the endpoint speaks OpenAI's schema; first-class presets are on the [roadmap](#roadmap) |\n\n<sub>**Verified** is measured, never asserted. ✅ Completion means `npm run verify:providers` listed that provider's models, sent a real request against a live key and got usage back, on the date shown. ⚪ Preset only means never probed — an absence of evidence, not a claim that it is broken. The dated evidence is committed under [`docs/provider-verification/`](docs/provider-verification/), and this table is generated from it by `npm run docs:providers`. Editing it by hand is undone by the next run, and CI fails if it drifts.</sub>\n\n<sub>**Publishes prices** decides whether Nexus can cost a request without you. Only **Groq** and **OpenRouter** return per-model prices in their own API; everywhere else a model arrives with no price, which Nexus flags rather than assuming zero — on the model row, when you add the pool, and on the Analytics page.</sub>\n\n<!-- END GENERATED PROVIDER TABLE -->\n\nAnthropic additionally gets a native `/v1/messages` endpoint, so Claude Code and the Anthropic SDKs\nwork unchanged.\n\n---\n\n## Architecture\n\n<div align=\"center\">\n\n<img src=\"./docs/assets/architecture.svg\" alt=\"Alayra Nexus request path — client to gateway (team auth, rate limiter, tiered router) to providers to telemetry\" width=\"100%\"/>\n\n</div>\n\nOne request enters authenticated and budget-checked, the router picks a healthy key by\ntier and cache affinity, the circuit breaker keeps a failing provider out of rotation, and\nusage is batched to PostgreSQL while live metrics land in Redis — all behind a single\nOpenAI-compatible URL.\n\nOnly the two stores at the bottom are swappable: configure neither and the same request path runs\nagainst a local SQLite file and in-process counters instead — see\n[Standalone mode](#standalone-mode--no-postgres-no-redis). Everything between the client and the\nproviders is identical either way.\n\n<details>\n<summary>Same diagram as plain text</summary>\n\n```\n  Your Application / IDE / Agent / Script\n           │\n           │  POST /v1/chat/completions\n           │  Authorization: Bearer <team-key>   ← optional, enables per-team analytics\n           ▼\n  ┌──────────────────────────────────────────────────────────┐\n  │                   Alayra Nexus Gateway                  │\n  │                                                          │\n  │   ┌───────────────┐          ┌─────────────────────────┐ │\n  │   │  Team Auth    │          │     Rate Limiter        │ │\n  │   │  SHA-256 hash │          │   RPM / TPM via Redis   │ │\n  │   └───────┬───────┘          └──────────┬──────────────┘ │\n  │           └─────────────┬───────────────┘                │\n  │                    ┌────▼───────┐                        │\n  │                    │   Router   │                        │\n  │                    │  Premium   │                        │\n  │                    │  Standard  │  ← tiered failover     │\n  │                    │   Fast     │                        │\n  │                    └────┬───────┘                        │\n  │        ┌────────────────┼──────────────┬──────────────┐  │\n  │        ▼                ▼              ▼              ▼  │\n  │    Anthropic          OpenAI        Google           Groq │\n  │   …and any other OpenAI-compatible endpoint you point at  │\n  └──────────────────────────────────────────────────────────┘\n           │\n           ▼\n    Token usage → async buffer → batched PostgreSQL write\n    Real-time metrics  → Redis\n    Analytics          → Admin Dashboard\n```\n\n</details>\n\n---\n\n## Quick Start\n\n### Option A — One command, nothing to provision\n\n```bash\nnpx @alayrasystems/nexus\n```\n\n**That single command downloads it and starts it.** There is no install step before it, and nothing\nto clean up after. The first run takes about a minute — most of it fetching the database engine —\nand npm shows only a spinner while it does, so give it that minute before deciding it has hung.\nEvery run after the first starts in seconds.\n\nThat is the whole thing. No clone, no build, no Postgres, no Redis, no Docker. It creates\n`~/.alayra-nexus`, generates its own encryption key and admin password, builds a SQLite database,\nand serves the **full dashboard** — provider pools, team keys, budgets, analytics, backup. Nothing\nis disabled because there is no database server; the engines underneath are different, the product\nis the same.\n\n```\n  Alayra Nexus 1.6.5 — first run\n\n  Data directory   /home/you/.alayra-nexus\n  Encryption key   /home/you/.alayra-nexus/secret.key  (generated)\n\n  ⚠  Back that key file up, somewhere other than this machine.\n     Without it the provider keys stored here can never be decrypted again.\n\n  Admin password   7Kq2vFm9Rt4xLn8p\n  Dashboard        http://127.0.0.1:3000\n```\n\nOpen the dashboard, claim it with that password, add a provider key, and point your app at\n`http://127.0.0.1:3000/v1`.\n\n| | |\n|---|---|\n| `--port 3001` | listen somewhere else |\n| `--host 0.0.0.0` | reachable from other machines (loopback only by default) |\n| `--data-dir ./nexus` | keep data somewhere other than your home directory |\n| `--env-file ./.env` | read configuration from a file you name |\n\n> [!NOTE]\n> **A `.env` in the current directory is not read.** It belongs to the project in that directory,\n> not to Nexus — and ten of the variables Nexus reads (`DATABASE_URL`, `ADMIN_PASSWORD`, `PORT`…)\n> have names common enough to appear in someone else's. If one is found, the gateway says so and\n> ignores it. Name the file with `--env-file` to use it deliberately.\n\nTo keep it around, install it instead of fetching it each time — the command it installs is short:\n\n```bash\nnpm install -g @alayrasystems/nexus\n```\n\n```bash\nalayra-nexus\n```\n\nStandalone is for evaluation, local development and CI — one process, one machine, and a restart\nclears sessions and rate-limit windows. When you outgrow it, [Backup & restore](#backup--restore)\ncarries everything into a Postgres deployment, provider keys included. See\n[Standalone mode](#standalone-mode--no-postgres-no-redis) for what you give up.\n\n### Option B — Published image (no clone, brings your own Postgres)\n\nA multi-arch image (amd64 + arm64) is published to **Docker Hub** and the **GitHub Container\nRegistry** from the same build, so the two are byte-identical — use whichever you prefer. If\nyou already have Postgres and Redis, run the gateway with one command:\n\n```bash\ndocker run -d --name alayra-nexus -p 3000:3000 \\\n  -e DATABASE_URL=\"postgresql://user:pass@host:5432/nexus\" \\\n  -e REDIS_URL=\"redis://host:6379\" \\\n  -e MASTER_ENCRYPTION_KEY=\"$(node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\")\" \\\n  -e ADMIN_PASSWORD=\"change-me\" \\\n  alayrasystems/nexus:latest\n```\n\n| Registry | Image |\n|---|---|\n| Docker Hub | `alayrasystems/nexus` |\n| GHCR | `ghcr.io/alayra-systems-pvt-limited/alayra-nexus` |\n\nPin a version for production (e.g. `:1.6.5`) rather than `:latest`.\n\n<details>\n<summary><b>Option C — Docker Compose (brings its own Postgres + Redis)</b></summary>\n\n\nNothing to clone and nothing to compile: Compose downloads the published image and\nstarts Postgres and Redis alongside it.\n\n```bash\ncurl -O https://raw.githubusercontent.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus/main/docker-compose.yml\n\n</details>\n\n# Two secrets. Keep MASTER_ENCRYPTION_KEY safe — without it your stored\n# provider keys can never be decrypted again.\ncat > .env <<EOF\nMASTER_ENCRYPTION_KEY=$(node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\")\nADMIN_PASSWORD=change-me\nNEXUS_VERSION=1.6.5\nEOF\n\ndocker compose up -d\n```\n\nDashboard is live at `http://localhost:3000`. The container applies its own database\nmigrations on startup, and prints your generated Nexus API key on first run —\n`docker compose logs nexus` to see it. **Save it then: the key is stored as a hash, so it is shown\nonce and never again** (lost it? rotate for a new one from **Connect**).\n\nOpen the dashboard and it will ask you to create your owner account, using the `ADMIN_PASSWORD` you\nset above — see [Accounts and roles](#accounts-and-roles).\n\n`DATABASE_URL` and `REDIS_URL` are set by Compose; you do not need to supply them.\nOmit `NEXUS_VERSION` to track `latest`, but pin it in production.\n\n<details>\n<summary>Building from source instead (contributors)</summary>\n\n```bash\ngit clone https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus.git\ncd alayra-nexus\ncp .env.example .env   # set MASTER_ENCRYPTION_KEY and ADMIN_PASSWORD\n\ndocker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build\n```\n\n</details>\n\n---\n\n<details>\n<summary><b>Option D — Railway (managed cloud, no server to run)</b></summary>\n\n\nThe gateway builds from the repo and serves the dashboard from a single service, so a\nmanaged deploy is three plugins and four variables:\n\n1. **New Project → Deploy from GitHub repo** → pick this repository. Railway builds the image.\n2. Add the **PostgreSQL** and **Redis** plugins to the project.\n3. On the gateway service → **Variables**, set (use each plugin's **private** connection URL):\n   - `DATABASE_URL` = `${{Postgres.DATABASE_URL}}`\n   - `REDIS_URL` = `${{Redis.REDIS_URL}}`\n   - `MASTER_ENCRYPTION_KEY` = 64 hex chars — generate fresh, never reuse:\n     `node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"`\n   - `ADMIN_PASSWORD` = a strong install secret (claims the first owner account)\n4. **Settings → Networking → Generate Domain** for a public `https://…up.railway.app` URL.\n\nRailway's proxy sets `X-Forwarded-Proto`, so the dashboard's **Connect** page prints the\ncorrect `https://` base URL with no extra config. Behind a proxy that doesn't, pin it with\n[`PUBLIC_URL`](#environment-variables). The container runs its own migrations on boot; open\nthe domain and it greets you with the owner-account setup screen.\n\n</details>\n\n<details>\n<summary><b>Option D — Manual setup (from source)</b></summary>\n\n\n**Prerequisites:** Node.js 22.12+, PostgreSQL 15+, Redis 7+\n\n```bash\ngit clone https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus.git\ncd alayra-nexus\n\nnpm install\n\ncp .env.example .env\n\n</details>\n\n# Edit .env with your values\n\n# Generate a secure MASTER_ENCRYPTION_KEY (run this once and save it):\nnode -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"\n\n# Postgres and Redis must be running. Don't have them locally? Start just the\n# two dependencies with Compose and run the gateway from source:\ndocker compose up -d postgres redis\n\n# Run database migrations\nnpm run migrate\n\n# Start\nnpm run dev          # development — hot reload via tsx\nnpm run build && npm start   # production\n```\n\nDashboard is live at `http://localhost:3000`\n\n> [!TIP]\n> **`Cannot reach Redis` / `Cannot reach PostgreSQL` on startup?** In server mode both are hard\n> dependencies — Redis holds rate-limit counters, circuit-breaker state, sticky\n> routing, budgets and the response cache; Postgres holds everything else. The\n> startup error names the one that's missing and the command that starts it.\n>\n> A gateway is only ever demoted to the local substitutes when you configure **neither** — never as\n> a reaction to an outage. If you set `DATABASE_URL` and it is unreachable, the gateway refuses to\n> start rather than quietly accepting traffic it is going to lose. See\n> [Standalone mode](#standalone-mode--no-postgres-no-redis).\n>\n> The dashboard is a Vite + Preact app in `web/`, built to static assets that the\n> gateway serves at `/` — there is no separate web server to run. To work on the\n> dashboard with hot reload, `cd web && npm run dev` (it proxies API calls to a\n> gateway running on `:3000`).\n\n---\n\n## Standalone mode — no Postgres, no Redis\n\nOne command and one SQLite file, with no services to provision — the honest list of what you give up by running it that way, where the data lives, how to tell which mode you are actually in, and when to move to server mode.\n\n**→ [docs/standalone.md](docs/standalone.md)**\n\n---\n\n## Backup & restore\n\nWhat a backup contains and what it deliberately leaves out, plus exporting and restoring from the dashboard and over the API.\n\n**→ [docs/backup.md](docs/backup.md)**\n\n---\n\n## Connect your tools\n\nAlayra Nexus speaks both the **OpenAI** API (`/v1/chat/completions`) and the\n**Anthropic Messages** API (`/v1/messages`), so almost any tool that lets you set a\ncustom base URL works — including Claude Code. You only need three values:\n\n- **Base URL:** `http://<your-host>:3000/v1`\n- **API key:** a team key from the dashboard (sent as `Authorization: Bearer <key>`, or `x-api-key: <key>`)\n- **Model:** `alayra-nexus-1`\n\n> [!NOTE]\n> **Cursor** (and some other cloud tools) route requests through their own servers, so\n> they cannot reach `http://localhost:3000` — they need a **publicly reachable HTTPS**\n> base URL. Local tools such as Cline, Continue.dev, and Claude Code call your gateway\n> directly and work against localhost. This is a Cursor constraint, not a Nexus one —\n> LiteLLM has the same requirement.\n\n### Claude Code\nClaude Code speaks the Anthropic Messages API. Point it at the gateway:\n\n```bash\nexport ANTHROPIC_BASE_URL=\"http://<your-host>:3000\"\nexport ANTHROPIC_AUTH_TOKEN=\"<your-team-key>\"\nclaude\n```\n\nRequests route through the same pool, failover, budgets, and analytics as everything\nelse. On startup Claude Code reads `GET /v1/models` to populate its model picker — which\nnow lists **your** configured models alongside the `alayra-nexus-1` auto-route entry, so\nyou can pick a specific one from inside the client or leave it on auto and let Nexus\nchoose.\n\n### Cursor\nSettings → **Models** → enable **OpenAI API Key**, paste your team key, tick **Override OpenAI Base URL** and set it to `http://<your-host>:3000/v1`. Add a custom model named `alayra-nexus-1`.\n\n### Cline / Roo Code (VS Code)\nAPI Provider → **OpenAI Compatible** → Base URL `http://<your-host>:3000/v1`, API Key = your team key, Model ID `alayra-nexus-1`.\n\n### Continue.dev\n```json\n{\n  \"models\": [\n    {\n      \"title\": \"Alayra Nexus\",\n      \"provider\": \"openai\",\n      \"model\": \"alayra-nexus-1\",\n      \"apiBase\": \"http://<your-host>:3000/v1\",\n      \"apiKey\": \"<your-team-key>\"\n    }\n  ]\n}\n```\n\n### OpenAI SDK — Python\n```python\nfrom openai import OpenAI\n\nclient = OpenAI(base_url=\"http://<your-host>:3000/v1\", api_key=\"<your-team-key>\")\nresp = client.chat.completions.create(\n    model=\"alayra-nexus-1\",\n    messages=[{\"role\": \"user\", \"content\": \"Hello\"}],\n)\nprint(resp.choices[0].message.content)\n```\n\n### OpenAI SDK — Node\n```js\nimport OpenAI from \"openai\";\n\nconst client = new OpenAI({\n  baseURL: \"http://<your-host>:3000/v1\",\n  apiKey: \"<your-team-key>\",\n});\nconst resp = await client.chat.completions.create({\n  model: \"alayra-nexus-1\",\n  messages: [{ role: \"user\", content: \"Hello\" }],\n});\nconsole.log(resp.choices[0].message.content);\n```\n\n### curl\n```bash\ncurl http://<your-host>:3000/v1/chat/completions \\\n  -H \"Authorization: Bearer <your-team-key>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"model\":\"alayra-nexus-1\",\"messages\":[{\"role\":\"user\",\"content\":\"Hello\"}]}'\n```\n\n> Streaming works everywhere — add `\"stream\": true` (or the client's streaming flag). Running Nexus behind TLS? Use your `https://…/v1` URL instead.\n\n---\n\n## Environment Variables\n\n| Variable | Required | Description |\n|---|---|---|\n| `DATABASE_URL` | Server mode | PostgreSQL connection string (`postgresql://user:pass@host:5432/db`). Leave unset for a local SQLite file — see [Standalone mode](#standalone-mode--no-postgres-no-redis) |\n| `REDIS_URL` | Server mode | Redis connection string (`redis://localhost:6379`). Leave unset for in-process counters |\n| `MASTER_ENCRYPTION_KEY` | Yes | 64 hex characters (32 bytes) — encrypts all stored API keys. Required in **both** modes |\n| `ADMIN_PASSWORD` | Yes | The gateway's deployment secret. Claims the first owner account on first run, and authorises a full reset. **Not** a day-to-day login once an owner exists — see [Accounts and roles](#accounts-and-roles). |\n| `NEXUS_MODE` | No | `server` or `standalone`. Normally unset — the mode is inferred from whether `DATABASE_URL` / `REDIS_URL` are set. Setting it makes the intent explicit, and the gateway **refuses to start** if it contradicts what you configured, rather than guessing which you meant |\n| `NEXUS_DATA_DIR` | No | Where standalone keeps its SQLite database (default: `.nexus` in the working directory). Ignored in server mode |\n| `PORT` | No | HTTP port (default: `3000`) |\n| `PUBLIC_URL` | No | The address the outside world reaches this gateway at — pins what the **Connect** page, quick-start snippets, and SSO `redirect_uri` print. Leave unset and the gateway infers it from the proxy's `X-Forwarded-Proto` / `X-Forwarded-Host` headers, or the Host header. Set it (e.g. `https://gateway.example.com`) when a proxy forwards the host but not the scheme, so a TLS deployment would otherwise print `http://`. |\n| `LOG_LEVEL` | No | Pino log level: `info`, `debug`, `warn` (default: `info`) |\n| `ABUSE_RATE_LIMIT_MAX` | No | Requests **per credential** per window before the abuse guard trips (default: `12000`). This is DoS/abuse protection, **not** a throughput cap — see [Rate limits, explained](#rate-limits-explained). |\n| `ABUSE_RATE_LIMIT_WINDOW` | No | Abuse-guard window (default: `1 minute`) |\n| `NEXUS_DEFAULT_MAX_TOKENS` | No | Output tokens reserved against a key's TPM budget when a request omits `max_tokens` (default: `2048`; reconciled to real usage afterward) |\n| `UPSTREAM_TTFT_MS` | No | Abort if a provider doesn't return response headers within this many ms (default: `20000`) |\n| `UPSTREAM_BODY_MS` | No | Non-streaming: max ms to read the full response body (default: `60000`) |\n| `UPSTREAM_STREAM_IDLE_MS` | No | Streaming: max ms gap between chunks before a hung stream is aborted (default: `30000`) |\n| `UPSTREAM_STREAM_MAX_MS` | No | Streaming: ceiling on one whole request in ms — the idle guard restarts on every chunk, so it bounds silence, not duration (default: `600000`) |\n\n> [!IMPORTANT]\n> Generate `MASTER_ENCRYPTION_KEY` with:\n> ```bash\n> node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"\n> ```\n> This key encrypts every provider API key stored in your database. Keep it secret. Keep a backup. Never reuse it across deployments.\n\n---\n\n## Rate limits, explained\n\nHow RPM and TPM are counted, what a request meets at the ceiling, and why the limit belongs to a key rather than a pool.\n\n**→ [docs/routing.md](docs/routing.md#rate-limits-explained)**\n\n---\n\n## Resilience & routing\n\nThe circuit breaker, cache-aware sticky routing, cost-aware routing, response caching, and what the gateway does — and does not — survive when Redis is unreachable.\n\n**→ [docs/routing.md](docs/routing.md#resilience--routing)**\n\n---\n\n## Teams & budgets\n\nGroup your scoped access keys into **teams**, and give each team a **USD budget cap**\nper day, week, or month. Enforcement happens on the admission path — before a request\never reaches a provider:\n\n- A key that belongs to a team over its budget gets **`429`** with the current spend,\n  the cap, and a `Retry-After` for when the window resets (UTC).\n- A **suspended** team's keys get **`403`** immediately.\n- Spend is tracked in Redis and **seeded from your real usage history**, so setting a\n  cap mid-month starts from what the team has actually spent — and budgets survive a\n  Redis restart.\n- Keys without a team (and teams without a cap) behave exactly as before — nothing\n  changes until you opt in.\n\n> [!NOTE]\n> Cost is only knowable after a response completes (streaming), so enforcement is\n> check-then-spend: requests already in flight when the cap is crossed can overshoot\n> it by their own cost. That's the standard trade for budget caps on a streaming\n> gateway.\n\nManage teams from the dashboard's **Teams** tab — create and budget teams, issue and\nassign scoped access keys, and read per-team usage — or drive the same operations over\nthe admin API (`/admin/teams`).\n\n---\n\n## BYOK — bring your own key\n\nA provider key can be **owned by a team** instead of living in the shared pool. An\nowned key serves only that team's traffic; nobody else can route through it. Set the\nowner when you add the key (**Pools → + Key → Owner**), or pass `ownerTeamId` to\n`POST /admin/providers/:providerId/keys`.\n\nRouting then works in two passes:\n\n1. **The team's own keys first**, in the usual tier order with LRU within a tier.\n2. **The shared pool**, but only if the team allows it.\n\nPer-team, `byokFallback` decides what happens when a team's own keys are all\nrate-limited, cooling, or banned:\n\n| `byokFallback` | Behaviour |\n|---|---|\n| `true` *(default)* | Fall back to the shared pool. Responses carry `X-Nexus-BYOK: true` only when an owned key served them |\n| `false` | **Hard isolation.** The request gets `503` + `Retry-After`. It never touches a credential the team did not bring |\n\nA BYOK key is **not a parallel proxy** — it is a scoped pool. Owned keys flow through\nthe exact same admission control, circuit breaker, guardrails, SSRF checks, and\nanalytics pipeline as pooled keys. There is one request path.\n\nTwo guarantees worth stating explicitly:\n\n- **A caller with no team can never be routed through an owned key**, even when the\n  shared pool is completely exhausted.\n- **The response cache is partitioned by owner.** A response produced by one team's\n  private key is never replayed to another team or to the shared pool, so an isolated\n  team only ever sees responses its own keys paid for.\n\nBYOK spend is still costed, attributed, and **counted against the team's budget cap** —\nset `budgetUsd: null` for a team that funds its own keys and shouldn't be capped.\n\n> [!WARNING]\n> Deleting a team **deletes its owned provider keys** along with it. This is\n> deliberate: releasing a private credential into the shared pool would let every\n> other caller route through it. The team's *access* keys survive, losing only their\n> budget cap.\n\nWatch adoption with the `nexus_byok_requests_total{result}` metric — a sustained\n`fallback` rate means a team is under-provisioned on its own credentials.\n\n---\n\n## API Reference\n\nEvery proxy and admin endpoint, with the request and response shapes.\n\n**→ [docs/api.md](docs/api.md)**\n\n---\n\n## Dashboard\n\nThe built-in web dashboard (served at `/`, a Vite + Preact app) gives you full operational\ncontrol — no CLI required for day-to-day work:\n\n<div align=\"center\">\n\n<img src=\"./docs/assets/health.png\" alt=\"Alayra Nexus Health dashboard — gateway process, Redis and PostgreSQL vitals, and readiness checks\" width=\"100%\"/>\n\n<sub><i>The <b>Health</b> tab — the gateway's own vitals: process, Redis, and PostgreSQL latency, cache-hit rates, and the readiness checks your load balancer sees.</i></sub>\n\n</div>\n\n\n- **Overview** — live gateway telemetry: request/token/cost trends, active keys and models, top teams, and recent admin activity by name\n- **Nexus** — provider pools and the model registry: per-key RPM utilization meters, add/test/ban keys, and each model's tier, capability flags, context window, and per-1M token pricing\n- **Connect** — the base URL (verified against your browser's own address bar), the API-key hint with one-click rotation, endpoint reference, and filled-in quick-start snippets\n- **Analytics** — request and token trend charts, stacked model breakdown, cost area chart, input/output comparison, team leaderboard, response-cache savings, CSV export, and a custom date-range picker\n- **Teams** — teams with budgets and routing tier, scoped access keys, and per-team usage stats\n- **Enterprise** — operator branding / white-labelling and per-company controls\n- **Security** — your sign-in security (password, TOTP two-factor with QR enrolment, active sessions) and admin API tokens\n- **Caching** — the optional exact-match response cache: toggle, TTL, and live hit-rate\n- **Health** — the gateway's own vitals: process, Redis, and PostgreSQL latency and readiness checks\n- **Logs** — the read-only audit trail: every state-changing action, who did it, and the result\n- **Settings** — system configuration: routing weights, guardrails, network/SSRF policy, notifications, and compliance/retention\n- **Admin** — people and roles (owner / admin / viewer), invites, single sign-on, and the factory reset\n\n---\n\n## Observability\n\nA Prometheus-compatible **`/metrics`** endpoint exposes the gateway's operational\nshape, so it drops straight into an existing ops stack.\n\n- **Metrics:** request rate and duration (by outcome and tier), upstream time-to-first-byte,\n  input/output tokens, prompt-cache (sticky) hit rate, per-provider request and error\n  rates (rate-limit / auth / server / timeout), pool utilization (active / cooling /\n  banned keys), plus standard Node process metrics (CPU, memory, event-loop lag, GC).\n- **Auth:** `/metrics` is **not** world-readable like `/health`. Scrape it with a bearer\n  token — set a dedicated **`METRICS_TOKEN`** (recommended), or it falls back to\n  `ADMIN_PASSWORD`. It is exempt from the abuse guard's rate limit but never from auth.\n\n```yaml\n# prometheus.yml\nscrape_configs:\n  - job_name: alayra-nexus\n    authorization:\n      credentials: <your METRICS_TOKEN>\n    static_configs:\n      - targets: ['your-host:3000']\n```\n\n<details>\n<summary><b>Distributed tracing (optional)</b></summary>\n\n\nThe gateway → provider call is wrapped in an OpenTelemetry span. It's a **no-op by\ndefault** (zero overhead); to collect traces, run the app with a standard OTel SDK and\npoint it at your collector — nothing to change in the code:\n\n```bash\nOTEL_EXPORTER_OTLP_ENDPOINT=http://your-collector:4318 \\\nnode --require @opentelemetry/auto-instrumentations-node/register dist/server.js\n```\n\n</details>\n\n## Security Model\n\n| Layer | Implementation |\n|---|---|\n| **Key encryption** | AES-256-GCM with a per-deployment `MASTER_ENCRYPTION_KEY`; plaintext keys never touch the database |\n| **Admin authentication** | Per-person accounts; email and password exchanged at `/admin/login` for a short-lived session token; optional per-user TOTP second factor; per-source lockout after repeated failures ([details](docs/security.md)) |\n| **Password hashing** | scrypt (memory-hard), per-user salt, cost parameters stored with the digest. The only human-chosen secret the gateway stores |\n| **Constant-time secrets** | The admin password and the metrics token are compared with `crypto.timingSafeEqual` over fixed-width digests, so rejection latency reveals nothing about the secret |\n| **Nexus API key hashing** | SHA-256; shown once when generated or rotated, never stored in the clear and never displayable again |\n| **Team key hashing** | SHA-256; plaintext shown once at creation, never stored |\n| **Audit attribution** | Every state-changing admin action records the account that performed it, by name — copied onto the record, so it outlives the account |\n| **HTTP hardening** | Fastify Helmet — `X-Frame-Options`, `X-Content-Type-Options`, HSTS, CSP headers |\n| **CORS** | Configurable origin allowlist |\n| **SSRF protection** | Outbound provider requests are restricted to http(s) **and** blocked from private/loopback/internal hosts by default ([details](docs/security.md)) |\n| **No telemetry** | Zero outbound calls to Alayra Systems or any third party. All data stays in your infrastructure |\n\n### Accounts and roles\n\nAccounts, the three roles, invites, single sign-on, recovery, two-factor authentication and lockout — plus SSRF protection and the optional content guardrails.\n\n**→ [docs/security.md](docs/security.md)**\n\n> [!WARNING]\n> Your `.env` file contains `MASTER_ENCRYPTION_KEY` and `ADMIN_PASSWORD`.  \n> Never commit it. This repository's `.gitignore` excludes `.env` by default.\n\n---\n\n## Roadmap\n\n- [x] Key pool management with AES-256-GCM encryption\n- [x] Multi-provider routing with tiered failover\n- [x] OpenAI-compatible proxy API with full streaming support\n- [x] Team key issuance with per-key RPM limits\n- [x] Admin dashboard — provider pools, model registry, team management\n- [x] Analytics — cost tracking, token trends, team leaderboard, CSV export\n- [x] Custom date range analytics\n- [x] Automated test suite and CI (lint, typecheck, test, build, audit)\n- [x] Circuit breaker (escalating cooldown, half-open probe) + cache-aware sticky routing\n- [x] SSRF protection — default-on private-host blocking with an opt-in allowlist\n- [x] Optional content guardrails — pluggable PII redaction and content/injection blocking\n- [x] Cost-aware routing — bias toward the cheapest healthy, in-headroom provider (tiebreaker)\n- [x] Atomic pre-admission rate limiting with real token accounting\n- [x] Per-key TPM enforcement, with reservation and post-response reconciliation\n- [x] Per-team budget caps with automatic cutoff\n- [x] Optional exact-match response caching\n- [x] Prometheus `/metrics` endpoint and optional OpenTelemetry tracing\n- [x] BYOK — team-owned provider keys with optional hard isolation\n- [x] Admin auth hardening — constant-time compare, login lockout, TOTP 2FA\n- [x] Standalone mode — SQLite and in-process memory, no Postgres and no Redis\n- [x] Encrypted backup and restore, portable across gateways and across engines\n- [x] A static, read-only live demo of the console\n- [ ] **CLI — coming soon.** A command-line interface over the existing admin API\n- [x] `npx @alayrasystems/nexus` — a published package that starts a gateway with no clone and no Docker\n- [x] Scheduled backups with retention and optional directory / mounted-volume copies\n- [ ] Object-storage backup destinations (S3 and GCS)\n- [x] Webhook and email alerts on key failure or budget threshold\n- [ ] Custom domain / CNAME support\n- [x] Integration and browser end-to-end test suites\n- [ ] Playground for single-model requests and side-by-side model comparison\n- [ ] Kubernetes Helm chart\n\n---\n\n## Contributing\n\nPull requests are welcome. For major changes, open an issue first to discuss the approach.\n\nPlease read [**CONTRIBUTING.md**](./CONTRIBUTING.md) for setup, the quality bar, and the PR\nprocess, and [**CODE_OF_CONDUCT.md**](./CODE_OF_CONDUCT.md) — participation is governed by the\nContributor Covenant. Security issues go to [SECURITY.md](./SECURITY.md), **not** a public issue.\n\n**Start here:** [`docs/architecture/PROJECT-STRUCTURE.md`](docs/architecture/PROJECT-STRUCTURE.md)\nexplains the layering rule and walks the full request path;\n[`docs/architecture/FILE-OVERVIEW.md`](docs/architecture/FILE-OVERVIEW.md) is a\nwhere-to-look index and a checklist for adding a feature.\n\nThe backend lives in `src/`, the admin dashboard in `web/` (Vite + Preact), and the\nend-to-end suite in `e2e/` (Playwright).\n\n```bash\n# Development\nnpm run dev\n\n# Type check\nnpx tsc --noEmit\n\n# Unit tests (backend, then dashboard)\nnpm test\ncd web && npm test\n\n# End-to-end: builds both packages, then drives the COMPILED gateway against a real\n# Postgres + Redis (docker compose up -d postgres redis) and a real browser. Uses its\n# own databases and Redis DBs — your local gateway's data is never touched.\ncd e2e && npm install && npx playwright install chromium && npx playwright test\n\n# Schema changes\nnpx prisma migrate dev --name your_migration_name\n```\n\n---\n\n## License\n\n[Apache License 2.0](./LICENSE) © 2026 Alayra Systems Pvt. Limited & Alayra Systems LLC.\n\n**Alayra Nexus™** is a trademark of Alayra Systems — see [TRADEMARK.md](./TRADEMARK.md).\nThe Apache 2.0 license covers the code; it does not grant rights to the name or logo.\n\n---\n\n<div align=\"center\">\n\n**Alayra Nexus™** is built by [Alayra Systems](https://github.com/Alayra-Systems-Pvt-Limited) —  \nsovereign AI infrastructure for teams who refuse to depend on someone else's cloud.\n\n</div>\n","readmeFilename":"README.md"}