Releases: Alayra-Systems-Pvt-Limited/Alayra-Nexus
Release list
v1.2.0 — Two-factor admin auth, BYOK, and the first image that actually ships the dashboard
Highlights
Two-factor authentication for the admin dashboard. Optional TOTP, ten single-use recovery codes, per-source login lockout, and revocable API tokens for scripts and CI.
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.
If you ran the v1.0.0 or v1.1.0 container image, the dashboard returned 404. This release fixes that. Please read the next section.
⚠️ The published image never contained the dashboard
The runtime stage of our Dockerfile copied the compiled server but not the dashboard's static files. @fastify/static only warns when its root directory is missing, so the container started cleanly, passed its healthcheck, and served the API correctly — while returning a bare 404 to anyone who opened it in a browser.
The failure was completely silent. If you tried docker run on v1.0.0 or v1.1.0, saw a 404, and concluded the project was broken: you were right, and we're sorry. The gateway itself was working the whole time.
v1.2.0 is the first published image that contains the dashboard.
docker pull ghcr.io/alayra-systems-pvt-limited/alayra-nexus:1.2.0Installations from source were never affected.
Admin authentication
Signing in now exchanges your password for a short-lived session token at POST /admin/login. The dashboard no longer keeps ADMIN_PASSWORD in browser storage.
Enable TOTP from Settings, or via the API:
# 1. Enrol — returns a secret and an otpauth:// URI for your authenticator app
curl -X POST -H "Authorization: Bearer $TOKEN" http://localhost:3000/admin/auth/totp/enrol
# 2. Confirm with a code — returns 10 single-use recovery codes
curl -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"code":"123456"}' http://localhost:3000/admin/auth/totp/confirmEnrolment does not take effect until a code confirms it, so an abandoned enrolment can never lock you out.
Important
Once 2FA is enabled, ADMIN_PASSWORD stops working as a bearer token on /admin/*.
This is deliberate. If the password still authenticated API calls, anyone holding it would bypass the second factor entirely and 2FA would be decorative. Scripts and CI should use an admin API token (POST /admin/tokens) — hashed, listed, and revocable.
Until you enrol a second factor, nothing changes. Upgrading is a no-op.
Lockout. Five failed sign-ins from one address triggers a 15-minute lockout with 429 + Retry-After (configurable via ADMIN_MAX_LOGIN_ATTEMPTS / ADMIN_LOCKOUT_SECONDS). A wrong password and a wrong code are reported identically, so the login form cannot be used as a password oracle.
TOTP is implemented directly on Node's crypto (RFC 6238 over RFC 4226) with no new dependency on the auth path, and is verified against both RFCs' published test vectors.
BYOK — bring your own key
A provider key can now be owned by a team instead of living in the shared pool. Set the owner when adding the key (Pools → + Key → Owner), or pass ownerTeamId to POST /admin/providers/:providerId/keys.
Routing tries the team's own keys first, then the shared pool — unless the team sets byokFallback: false, in which case it is hard-isolated and receives 503 rather than a credential it did not bring.
Owned keys are a scoped pool, not a parallel proxy: they flow through the same admission control, circuit breaker, guardrails, SSRF checks, and analytics pipeline. Two guarantees:
- A caller with no team can never be routed through an owned key, even when the shared pool is exhausted.
- The response cache is partitioned by owner. A response produced by one team's private key is never replayed to another team or to the shared pool.
Responses carry X-Nexus-BYOK: true, and nexus_byok_requests_total{result} tracks own / fallback / isolated_block.
Security
- The admin password, the Nexus API key, and the metrics token are now compared with
crypto.timingSafeEqualover fixed-width digests.===on strings short-circuits at the first differing byte, so rejection latency leaked how many leading bytes of a guess were correct. Team keys were already safe. - Provider names no longer reach inline
onclickhandlers. HTML escaping does not protect a JavaScript string context — a browser decodes an attribute before parsing its contents as code — so a provider namedO'Reilly'); …could break out. Values now travel indata-attributes read by a delegated listener.
Also in this release
- Response caching (optional, off by default) — an identical request is served from Redis, skipping the provider entirely. A real $0 call.
- A missing Postgres or Redis now fails with an instruction, not a retry storm. Startup names the unreachable service, its host and port, and the command that starts it. Connection URLs are reduced to
host:port, so a password inREDIS_URLnever reaches your logs. GET /admin/routing/statusreports per-provider key counts. They were previously summed across a whole tier and stamped onto every provider in it.X-Nexus-Tier-Downgradenow means what it says: a higher tier existed and could not serve the request. It was previously set on every non-premium request, including deployments with no premium provider configured.- Architecture docs at
docs/architecture/— the layering rule, the full request path, and a where-to-look file index. - The dashboard moved from
public/tofrontend/, and the admin API from one 571-line file tosrc/routes/admin/split by resource. No endpoint, request, or response changed.
Upgrading
docker compose pull && docker compose up -dThe container applies migration 0005_admin_auth automatically at startup. Running from source, apply it yourself:
npm run migrateThe response cache key changed to add tenant scoping, so cached entries from an earlier version are ignored and the cache repopulates over one TTL. Nothing else requires action.
Full changelog: v1.1.0...v1.2.0
Alayra Nexus v1.1.0 — Teams & Budgets, Observability, and an Important Install Fix
The first feature release since launch — and an important one to upgrade to.
⚠️ Important fix — fresh installs
v1.0.0's startup migration step silently applied nothing, so a fresh docker run against an empty database got no tables. v1.1.0 fixes the migration pipeline: the container now provisions and upgrades the database correctly at startup. If you deployed v1.0.0, upgrade to :1.1.0; existing databases created by the old path baseline once — see the CHANGELOG.
👥 Teams & budget caps
Group scoped access keys into teams and give each a USD budget per day, week, or month — enforced before a request ever reaches a provider:
- Over budget →
429with current spend, the cap, and aRetry-Afterfor the window reset. - Suspended team →
403immediately. - Spend is seeded from your real usage history, so a cap set mid-month starts from actual spend — and survives a Redis restart.
- Keys without a team behave exactly as before. Manage it all via
GET/POST/PATCH/DELETE /admin/teams.
📊 Observability
- Prometheus
/metrics— request rate & latency by outcome and tier, upstream time-to-first-byte, tokens, prompt-cache hit rate, per-provider error rates, pool utilization, process metrics. Auth-guarded viaMETRICS_TOKEN(never world-readable), exempt from rate limiting. - OpenTelemetry (optional) — the gateway→provider call is traced when you attach an OTel SDK; zero overhead otherwise.
🔌 Connect your tools
New README guide with copy-paste setup for Cursor, Cline / Roo Code, Continue.dev, the OpenAI SDK (Python & Node), and curl.
🚀 Upgrade / install
docker run -d --name alayra-nexus -p 3000:3000 \
-e DATABASE_URL="postgresql://user:pass@host:5432/nexus" \
-e REDIS_URL="redis://host:6379" \
-e MASTER_ENCRYPTION_KEY="$(openssl rand -hex 32)" \
-e ADMIN_PASSWORD="change-me" \
ghcr.io/alayra-systems-pvt-limited/alayra-nexus:1.1.0
Or docker compose up -d from a clone. Full details: CHANGELOG.md
Alayra Nexus v1.0.0
First tagged release of Alayra Nexus™ — an open-source AI gateway. Pool your provider keys behind one OpenAI-compatible endpoint with load balancing, failover, circuit breaking, rate limits, guardrails, and cost analytics. Self-host in minutes.
🚀 Quick start
Published multi-arch image (amd64 + arm64):
docker run -d --name alayra-nexus -p 3000:3000 \
-e DATABASE_URL="postgresql://user:pass@host:5432/nexus" \
-e REDIS_URL="redis://host:6379" \
-e MASTER_ENCRYPTION_KEY="$(openssl rand -hex 32)" \
-e ADMIN_PASSWORD="change-me" \
ghcr.io/alayra-systems-pvt-limited/alayra-nexus:1.0.0
Or run docker compose up -d from a clone (brings its own Postgres + Redis). Dashboard at http://localhost:3000/dashboard.
✨ Highlights
- OpenAI-compatible proxy with full streaming — one model name,
alayra-nexus-1, routed across your pool. - Real admission control — atomic per-key RPM/TPM (Redis Lua), a real tokenizer, TPM reservation + reconciliation, upstream timeouts.
- Circuit breaker — escalating cooldown, half-open recovery probe, separate 429 handling, auto-ban on repeated auth failures.
- Cache-aware sticky routing — multi-turn conversations reuse the provider's prompt cache.
- Cost-aware routing (optional) — cheapest healthy, in-headroom provider as a tiebreaker that never overrides health or cache affinity.
- Content guardrails (optional) — pluggable PII redaction / content + injection blocking.
- SSRF protection — private/internal hosts blocked by default, with an opt-in allowlist.
- Async analytics pipeline — usage batched off the request path.
- Admin dashboard — provider pools, model registry, team keys, analytics, and settings.
🔒 Notes
- Licensed under Apache-2.0; secrets encrypted at rest (AES-256-GCM); SSRF blocking on by default.
- Roadmap: constant-time admin-auth comparison and 2FA (Phase 6) are not yet in place — protect your admin password and API key accordingly.
Full changelog: https://github.com/Alayra-Systems-Pvt-Limited/Alayra-Nexus/blob/main/CHANGELOG.md