The production frontend calls backend-rust/apps/users/src/handlers/ai.rs
directly. This service is an orphaned parallel build. Added a prominent
banner in main.rs pointing devs to the correct extension points.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Chat and ticket-creation endpoints now scope lookups/attribution to
the authenticated JWT identity instead of trusting a client-supplied
user_id in the request body (IDOR)
- Add per-user in-memory rate limiter on AI-generation endpoints to
guard against unbounded LLM-cost abuse
- Bind confirm_action to the authenticated user for audit logging
- Bump vulnerable transitive dependencies via cargo update
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a ReferenceLookupProvider that calls the backend's new
GET /api/support/reference/{number} endpoint, and wires intent detection
so a customer mentioning a reference number like NXT-VER-26-000042 gets
that record's status looked up and summarized directly in chat.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same fix as nxtgauge-frontend-solid and nxtgauge-admin-solid: this
script isn't currently wired into this repo's build.yaml, but it's
identical to the version that deleted frontend-solid's just-built image
seconds after pushing it (sorted candidates by request time instead of
actual build time). Fixing it here too so it's safe if/when it gets
wired up, rather than leaving a dormant copy of the same bug.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
git diff --quiet (no --cached) compares the working tree to the index,
not the index to HEAD. It ran right after git add, at which point the
working tree always matches the index - so it reported "no changes"
unconditionally, on every single run, regardless of whether the sed
substitution actually changed anything relative to HEAD. This silently
skipped the gitops commit+push every time (job still exited 0), which
is why the deployed digest never advanced across ~15 build runs.
Switched to git diff --cached --quiet, and added step-by-step echo
diagnostics so a future silent failure is visible in the run log
instead of just vanishing between two log lines.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The gitops-update sed only matched the old tag-based image reference
(image: ...:SHA). After the digest-pinning fix in nxtgauge-gitops
(release-patch.yaml now uses @sha256:... to stop Flux's
ImageUpdateAutomation from racing this file), the regex silently
matched nothing, git diff showed no change, and every build since
has skipped deploying - the cluster stayed on an old digest despite
CI reporting success. Now captures the pushed image's digest via
buildx's --metadata-file and writes @sha256:<digest> directly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
complete_as and complete_as_user were passing internal logical model names
(jd-generator, profile-writer, etc.) directly to LiteLLM, which only
knows the configured model alias (askash-main). Route all completions
through self.model so LiteLLM can resolve the correct backend model.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Third failure: ashwin/nxtgauge-gitops's server-side HEAD symref is broken
("remote HEAD refers to nonexistent ref" on plain clone, even though
refs/heads/main exists via the API) - a Forgejo-level repo quirk, not
something to fix here. backend-rust's workflow survives this by accident
(it does an explicit git checkout after cloning); clone with -b main
directly instead of relying on HEAD resolution at all.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Second failure: pushing to ci.nxtgauge.com/nxtgauge/nxtgauge-ai-assistant
404'd because no "nxtgauge" org exists on this Forgejo instance. The image
this app actually runs (verified against the live k8s deployment) lives
under ci.nxtgauge.com/ashwin/nxtgauge-ai-assistant, and the real GitOps
target is apps/nxtgauge-ai-assistant/overlays/prod/release-patch.yaml in
ashwin/nxtgauge-gitops (which Flux's image-automation-controller also
tracks via its $imagepolicy marker), not base/deployment.yaml.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
First real run after fixing the registry/secrets config failed immediately
with "Cannot connect to the Docker daemon at tcp://127.0.0.1:2375" - the
job container is nested inside the runner pod's dind sidecar, so its own
loopback isn't the sidecar's. Port the gateway-detection step already
working in nxtgauge-backend-rust's workflow.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The GitOps step relied on GITEOPS_REPO/GITEOPS_SSH_KEY secrets that were
never configured, and the registry login used a REGISTRY_HOSTPORT secret
that doesn't match this repo's actual registry setup. Switch to the
Forgejo registry directly and push the GitOps update over HTTPS with a
token, matching how other services in this org already deploy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds UserKeyClient, which fetches (and caches) an account's LiteLLM virtual
key from the users service's internal endpoint. AiProvider gains
complete_as_user(), and LiteLLMProvider resolves the caller's key when
available, falling back to the master key otherwise so existing behavior
is unchanged where no per-account key is wired up yet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Remove build-and-deploy-forgejo.yml from GitHub Actions
- Forgejo CI at ci.nxtgauge.com now handles all builds and deployments
- All images pushed to Forgejo registry (ci.nxtgauge.com/ashwin/*)
- Update workflow to push to ci.nxtgauge.com/ashwin/nxtgauge-ai-assistant
- Use FORGEJO_USERNAME and FORGEJO_TOKEN secrets
- Fix gitops repo cloning to use x-access-token
jsonwebtoken v10's default features are just use_pem - neither of its
crypto backend features (rust_crypto/aws_lc_rs) is enabled by default,
so decode::<Claims>() panicked on every real bearer token (not caught
by earlier testing, which only exercised the no-token 401 path).
backend-rust's own crates already enable rust_crypto for the same
crate; matches that here.
Wires up 8 previously missing/stub Ask Ash capabilities: resume
improvement, job post improvement, professional/jobseeker/company
profile improvement, service description generation, KB
article/notification writing, admin support ticket summarization, and
lead/credit guidance. Also upgrades explain_plan_limits and
check_ai_pack_balance from static canned strings to real generated
answers.
Adds AiProvider::complete_as(model, ...) so callers can target
specific LiteLLM model aliases (jd-generator, profile-writer,
service-writer, support-drafter, decision-support, askash-main/fast)
that were already defined in apps/litellm/base/configmap.yaml but
never actually used by ai-assistant, since it wasn't even configured
to use the litellm provider (defaulted to plain Ollama with the tiny
gemma3:270m model for every task, with no LLM_PROVIDER/LITELLM_* env
vars set in the deployment).
New content_tools module holds the shared generation logic; new
routes registered for each feature; KB content generation and support
ticket summarization are gated to ADMIN/EMPLOYEE roles via JWT claims.
Registry gains 4 new ActionDefinitions (improve_job_post,
generate_kb_content, lead_credit_guidance, ai_auto_apply_status) to
match the existing registry pattern.
require_auth and NxtgaugeTicketProvider::create_ticket both called
env::var(...).expect(...) on every request, crashing instead of
returning a proper error when JWT_SECRET/AI_SERVICE_KEY were unset.
Both are now loaded once into AppConfig at startup and checked with
a graceful 401/500 response.
- Add src/auth.rs with require_auth middleware extracting user_id from Bearer token
- Wire auth middleware onto all /api/* routes
- Replace CorsLayer::permissive() with env-driven FRONTEND_URL/ADMIN_URL origins
- Pass real auth_user.user_id to confirm_action instead of conversation_id
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>