From 90d2cf83269b10929628d038f07fb6b79457afa5 Mon Sep 17 00:00:00 2001 From: gramps Date: Mon, 6 Jul 2026 19:56:32 -0700 Subject: [PATCH] =?UTF-8?q?Rename:=20jarvisChat=20=E2=86=92=20cAIc=20(prod?= =?UTF-8?q?uct=20name)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - jarvisChat/JarvisChat/jarvischat → cAIc/cAIc/caic (branded/lower) - JARVISCHAT_ env vars → CAIC_ - jc- script/config prefix → caic- - jarvis_rag → caic_rag - jarvischat.db / volumes → caic.db / caic_* - AMQP vhost/user jarvischat → caic - Syslog, loggers, docstrings all updated - 47 files, zero stale references, 148 tests pass --- AGENTS.md | 16 +-- CLAUDE.md | 16 +-- README.md | 44 ++++---- TASKS.md | 100 +++++++++--------- amqp.py | 4 +- app.py | 12 +-- auth.py | 4 +- cluster.py | 4 +- config.py | 24 ++--- db.py | 12 +-- docker.md | 88 +++++++-------- docs/wiki/Developer-Architecture.md | 34 +++--- docs/wiki/current-wip.md | 6 +- eviction.py | 4 +- gpu.py | 4 +- hardware.py | 4 +- memory.py | 4 +- rag.py | 4 +- routers/chat.py | 2 +- routers/completions.py | 2 +- routers/conversations.py | 2 +- routers/ingest.py | 2 +- routers/models.py | 2 +- routers/rag_admin.py | 2 +- routers/search_route.py | 2 +- routers/upload.py | 2 +- search.py | 4 +- security.py | 4 +- templates/index.html | 16 +-- tests/test_auth_capabilities.py | 4 +- tests/test_chat_streaming_and_memory_paths.py | 4 +- tests/test_completions.py | 6 +- tests/test_conversations.py | 4 +- tests/test_error_envelopes.py | 4 +- tests/test_hardware.py | 12 +-- tests/test_ingest.py | 6 +- tests/test_ip_allowlist.py | 4 +- tests/test_memories.py | 4 +- tests/test_models_router.py | 4 +- tests/test_presets.py | 4 +- tests/test_profile.py | 4 +- tests/test_rag_management.py | 6 +- tests/test_rate_and_payload_guardrails.py | 4 +- tests/test_search_route.py | 4 +- tests/test_settings_allowlist.py | 4 +- tests/test_skills_framework.py | 4 +- tests/test_upload.py | 4 +- 47 files changed, 255 insertions(+), 255 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ce123ec..d16e8c3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ -# JarvisChat — Agents Guide +# cAIc — Agents Guide ## Run @@ -60,8 +60,8 @@ Refactored from single-file (`app.py`) into modules under project root: ### Entrypoint / API keys - `app.py` line 148: `uvicorn.run(app, ...)` when called directly -- `config.py` line 14: `LLAMA_SERVER_BASE` defaults to `http://192.168.50.108:8081` — llama-server on ultron, RPC-offloads GPU layers to jarvis :50052 -- `config.py` line 17: `COMPLETIONS_API_KEY` read from `JARVISCHAT_COMPLETIONS_API_KEY` env var or auto-generates +- `config.py` line 14: `LLAMA_SERVER_BASE` defaults to `http://192.168.50.108:8081` — llama-server on coordinator, RPC-offloads GPU layers to worker :50052 +- `config.py` line 17: `COMPLETIONS_API_KEY` read from `CAIC_COMPLETIONS_API_KEY` env var or auto-generates - `config.py` line 13: `OLLAMA_BASE` is legacy/unused — all endpoints use `LLAMA_SERVER_BASE` ### Key flows @@ -83,11 +83,11 @@ The upstream request includes `"logprobs": true`. `parse_llama_stream_chunk()` e - `/api/ingest` is exempt from session auth — self-authenticates via Bearer token - IP allowlist, rate limiting, origin checking, payload size limits — all enforced in `app.py` middleware - Origin check applies to **all** `/api/` requests; returns `False` when both `Origin` and `Referer` are absent -- `JARVISCHAT_ADMIN_PIN` env var required on first boot (or `JARVISCHAT_ALLOW_DEFAULT_PIN=true`) +- `CAIC_ADMIN_PIN` env var required on first boot (or `CAIC_ALLOW_DEFAULT_PIN=true`) ### Database -- SQLite at `jarvischat.db`, auto-created by `init_db()` on startup via FastAPI `lifespan` +- SQLite at `caic.db`, auto-created by `init_db()` on startup via FastAPI `lifespan` - `get_db()` opens new connection per request (no pool). Close after use. - FTS5 virtual table `memories` for full-text search with BM25 ranking. - `upload_context` table: auto-expiring document storage for chat context injection. @@ -96,11 +96,11 @@ The upstream request includes `"logprobs": true`. `parse_llama_stream_chunk()` e | Service | Required | Port | |---------|----------|------| -| llama-server (ultron) | Yes | 8081 + RPC :50052 (jarvis GPU) | +| llama-server (coordinator) | Yes | 8081 + RPC :50052 (worker GPU) | | SearXNG | No | 8888 | | wttr.in | No | weather shortcut | | rocm-smi | No | AMD GPU stats | -| Qdrant | No | 6333 (ultron) — RAG vector search | +| Qdrant | No | 6333 (coordinator) — RAG vector search | ### Config quirks @@ -108,7 +108,7 @@ The upstream request includes `"logprobs": true`. `parse_llama_stream_chunk()` e - `SUPPORTED_UPLOAD_TYPES` includes images (png/jpeg/gif/svg/webp) + text + PDF + JSON - `UPLOAD_CONTEXT_EXPIRY_HOURS` = 1 hour - Rate limits and payload caps in `config.py` — patch `security.RL_*` not `config.RL_*` for tests -- RAG embedding requests go to `EMBED_URL` at `/api/embeddings` (Ollama on jarvis :11434) +- RAG embedding requests go to `EMBED_URL` at `/api/embeddings` (Ollama on worker :11434) ### SSE Protocol diff --git a/CLAUDE.md b/CLAUDE.md index 2759a29..6e5ac9a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,7 +9,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ./venv/bin/uvicorn app:app --host 0.0.0.0 --port 8080 --reload # Production (via systemd) -sudo systemctl restart jarvischat +sudo systemctl restart caic # Direct run ./venv/bin/python app.py @@ -24,7 +24,7 @@ sudo systemctl restart jarvischat ## Architecture -Modular FastAPI app — `app.py` wires routers, middleware, and lifespan. SQLite database auto-created at `jarvischat.db` on first run. No build step, single `templates/index.html`. +Modular FastAPI app — `app.py` wires routers, middleware, and lifespan. SQLite database auto-created at `caic.db` on first run. No build step, single `templates/index.html`. ### Request Flow: `/api/chat` @@ -54,22 +54,22 @@ FTS5 virtual table (`memories`) in SQLite. `search_memories()` uses BM25 ranking ### Key Constants (`config.py`) -- `LLAMA_SERVER_BASE` — `http://192.168.50.108:8081` (ultron llama-server, RPC offloads to jarvis GPU) +- `LLAMA_SERVER_BASE` — `http://192.168.50.108:8081` (coordinator llama-server, RPC offloads to worker GPU) - `SEARXNG_BASE` — `http://localhost:8888` - `PERPLEXITY_THRESHOLD` — `15.0` -- `EMBED_URL` — `http://192.168.50.210:11434/api/embeddings` (Ollama on jarvis) +- `EMBED_URL` — `http://192.168.50.210:11434/api/embeddings` (Ollama on worker) - `VERSION` — current version string ### External Services | Service | Required | Port | |---------|----------|------| -| **llama-server** (ultron) | Yes | 8081 + RPC :50052 (jarvis GPU) | +| **llama-server** (coordinator) | Yes | 8081 + RPC :50052 (worker GPU) | | **SearXNG** | No | 8888 | | **wttr.in** | No | weather shortcut | | **rocm-smi** | No | AMD GPU stats | -| **Qdrant** (ultron) | No | 6333 — RAG vector search | -| **Ollama** (jarvis) | No | 11434 — embeddings only | +| **Qdrant** (coordinator) | No | 6333 — RAG vector search | +| **Ollama** (worker) | No | 11434 — embeddings only | ### Database @@ -86,4 +86,4 @@ All streaming endpoints yield `data: {json}\n\n`. Key event shapes: ### Deployment -Runs as systemd service under user `jarvischat`, working directory `/opt/jarvischat`. Logs via syslog (`journalctl -u jarvischat`). Version bumps via git tag + commit, deployed via `git pull && systemctl restart jarvischat`. +Runs as systemd service under user `caic`, working directory `/opt/caic`. Logs via syslog (`journalctl -u caic`). Version bumps via git tag + commit, deployed via `git pull && systemctl restart caic`. diff --git a/README.md b/README.md index 52692d6..f4ad12a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# jarvisChat v0.14.0 +# cAIc v0.14.0 You have a garage full of retired office PCs, a GPU that was mid-range when Obama was president, and a burning desire to chat with a language model without renting some billionaire's server farm. Congratulations — you've found your people. -jarvisChat is a chat UI that grew limbs. It started as a single-file Python script because OpenWebUI wouldn't install on Debian 13, and somewhere along the way it learned to file paperwork (file attachments), write things down (RAG ingest), boss around other computers (AMQP clustering), and check its own pulse (hardware self-assessment). It now does all the things you didn't ask for, plus a few you might actually use. +cAIc is a chat UI that grew limbs. It started as a single-file Python script because OpenWebUI wouldn't install on Debian 13, and somewhere along the way it learned to file paperwork (file attachments), write things down (RAG ingest), boss around other computers (AMQP clustering), and check its own pulse (hardware self-assessment). It now does all the things you didn't ask for, plus a few you might actually use. Under the hood: FastAPI + SQLite + Jinja2 on Python 3.13. Distributes inference across mismatched hardware via llama.cpp RPC. @@ -38,7 +38,7 @@ Developer wiki: [docs/wiki/Home.md](docs/wiki/Home.md) ### Terminal RAG Hook — `POST /api/ingest` (v0.11.0) - Bearer token auth (same key as `/v1/chat/completions`) - Chunking via shared `chunk_text()` helper, embed via Ollama, upsert to Qdrant -- `jc-ingest.sh` — PROMPT_COMMAND shell script for autonomous terminal history ingestion +- `caic-ingest.sh` — PROMPT_COMMAND shell script for autonomous terminal history ingestion ### v1.8.0 Foundation (refactor & fixes) - **Modular refactor** — single-file `app.py` split into `config.py`, `db.py`, `auth.py`, `security.py`, `memory.py`, `search.py`, `rag.py`, `gpu.py`, and `routers/` package @@ -64,7 +64,7 @@ Developer wiki: [docs/wiki/Home.md](docs/wiki/Home.md) ## File Structure ``` -/opt/jarvischat/ +/opt/caic/ ├── amqp.py # aio-pika AMQP connection manager + subscribe/rebind ├── app.py # FastAPI app entry point ├── cluster.py # Cluster protocol: node registry, event log, ping/pong @@ -112,45 +112,45 @@ Developer wiki: [docs/wiki/Home.md](docs/wiki/Home.md) ```bash # Create directory and venv -sudo mkdir -p /opt/jarvischat -sudo chown $USER:$USER /opt/jarvischat -cd /opt/jarvischat +sudo mkdir -p /opt/caic +sudo chown $USER:$USER /opt/caic +cd /opt/caic python3 -m venv venv # Install dependencies pip install fastapi uvicorn httpx psutil jinja2 python-multipart pypdf aio-pika # Set admin PIN before first startup (4 digits) -export JARVISCHAT_ADMIN_PIN=4827 +export CAIC_ADMIN_PIN=4827 # Create subdirectories mkdir -p templates static # Copy files -# (copy all .py files to /opt/jarvischat/) -# (copy routers/ directory to /opt/jarvischat/) -# (copy templates/index.html to /opt/jarvischat/templates/) +# (copy all .py files to /opt/caic/) +# (copy routers/ directory to /opt/caic/) +# (copy templates/index.html to /opt/caic/templates/) ``` WARNING: Do not use `1234` as your admin PIN unless you accept weak local security. -NOTE: First boot requires `JARVISCHAT_ADMIN_PIN` unless you explicitly opt into insecure fallback with `JARVISCHAT_ALLOW_DEFAULT_PIN=true`. +NOTE: First boot requires `CAIC_ADMIN_PIN` unless you explicitly opt into insecure fallback with `CAIC_ALLOW_DEFAULT_PIN=true`. ## Systemd Service -Create `/etc/systemd/system/jarvischat.service`: +Create `/etc/systemd/system/caic.service`: ```ini [Unit] -Description=jarvisChat - Local Inference Web Interface +Description=cAIc - Local Inference Web Interface After=network.target [Service] Type=simple -User=jarvischat -Group=jarvischat -WorkingDirectory=/opt/jarvischat -ExecStart=/opt/jarvischat/venv/bin/uvicorn app:app --host 0.0.0.0 --port 8080 +User=caic +Group=caic +WorkingDirectory=/opt/caic +ExecStart=/opt/caic/venv/bin/uvicorn app:app --host 0.0.0.0 --port 8080 Restart=always RestartSec=5 @@ -160,8 +160,8 @@ WantedBy=multi-user.target ```bash sudo systemctl daemon-reload -sudo systemctl enable jarvischat -sudo systemctl start jarvischat +sudo systemctl enable caic +sudo systemctl start caic ``` ## Memory Commands @@ -171,7 +171,7 @@ In chat, natural language triggers memory operations: | You say | What happens | |---------|--------------| | "remember that I prefer Rust over Go" | Stores as `preference` | -| "remember that JarvisChat runs on port 8080" | Stores as `infrastructure` | +| "remember that cAIc runs on port 8080" | Stores as `infrastructure` | | "note that the deadline is Friday" | Stores as `general` | | "forget about the deadline" | Removes matching memories | @@ -317,4 +317,4 @@ MIT ## Repository -Gitea: `ssh://gitea@llgit.llamachile.tube:1319/gramps/jarvisChat.git` +Gitea: `ssh://gitea@llgit.llamachile.tube:1319/gramps/caic.git` diff --git a/TASKS.md b/TASKS.md index 86a2c6b..d417bb3 100644 --- a/TASKS.md +++ b/TASKS.md @@ -1,4 +1,4 @@ -# jarvisChat — OpenCode Prompt Sequence +# cAIc — OpenCode Prompt Sequence # Generated: 2026-07-01 # Execute sequentially. Run full test suite after each task before proceeding. # Test command: ./venv/bin/python -m pytest tests/ -v @@ -7,13 +7,13 @@ ## ~~TASK 1 — README Cleanup [DONE]~~ -Review README.md in the current repo. Remove any node references other than `ultron` (192.168.50.108) and `jarvis` (192.168.50.210). Ensure all references to the project use the exact casing `jarvisChat` — not `Jarvischat`, `JarvisChat`, or `jarvischat`. Do not change any functional content, endpoint documentation, or architecture descriptions — this is a text cleanup only. After editing, verify the file renders cleanly as markdown. Commit with message: `docs: clean up node references and branding consistency`. +Review README.md in the current repo. Remove any node references other than `coordinator` (192.168.50.108) and `worker` (192.168.50.210). Ensure all references to the project use the exact casing `cAIc` — not `Jarvischat`, `JarvisChat`, or `jarvischat`. Do not change any functional content, endpoint documentation, or architecture descriptions — this is a text cleanup only. After editing, verify the file renders cleanly as markdown. Commit with message: `docs: clean up node references and branding consistency`. No new tests required for this task. --- -## ~~TASK 2 — Qwen2.5-Coder llama-server Service on Ultron (Infrastructure) [DONE]~~ +## ~~TASK 2 — Qwen2.5-Coder llama-server Service on Coordinator (Infrastructure) [DONE]~~ **Status: Systemd unit created, verified, and restored.** @@ -24,8 +24,8 @@ This task originally defined creation of `/etc/systemd/system/llama-server-coder 1. **Task 13** — Phi-4-mini triage (`triage.py`) classifies the query as `general`, `code`, `search`, or `rag` 2. **Task 13** — `select_node()` picks the best worker node; if the ideal model isn't active, it triggers a swap 3. **Task 14** — `request_model_swap()` publishes `cmd.swap_model` via AMQP `jc.admin` exchange -4. **Task 12** — The node agent on jarvis receives the command, stops the current llama-server, starts the correct one, waits for health, and publishes `model_ready` -5. **Task 14** — ultron receives `model_ready`, updates the cluster registry, and routes the query to the node +4. **Task 12** — The node agent on worker receives the command, stops the current llama-server, starts the correct one, waits for health, and publishes `model_ready` +5. **Task 14** — coordinator receives `model_ready`, updates the cluster registry, and routes the query to the node The swap is async and transparent — the user sees only latency. The UI (Task 15) shows a yellow "swapping" status dot during the transition. @@ -39,7 +39,7 @@ No pytest tests required for this infrastructure task. ## ~~TASK 3 — Update OpenCode Config to Use Qwen on :8082 [DONE]~~ -Update `/home/gramps/.config/opencode/opencode.jsonc` (on this machine, ultron) to point the configured provider at `http://127.0.0.1:8082/v1` instead of `http://127.0.0.1:8081/v1`. The model name in the config should be updated to reflect `qwen2.5-coder-14b` or whatever model ID the llama-server instance at :8082 reports via `/v1/models`. Verify the endpoint is reachable before writing the config change. Do not restart OpenCode — the config change takes effect on next session start. +Update `/home/gramps/.config/opencode/opencode.jsonc` (on this machine, coordinator) to point the configured provider at `http://127.0.0.1:8082/v1` instead of `http://127.0.0.1:8081/v1`. The model name in the config should be updated to reflect `qwen2.5-coder-14b` or whatever model ID the llama-server instance at :8082 reports via `/v1/models`. Verify the endpoint is reachable before writing the config change. Do not restart OpenCode — the config change takes effect on next session start. No pytest tests required for this task. @@ -52,7 +52,7 @@ No pytest tests required for this task. This task implements the backend half of file/document attachment (TODO #21). The goal is dual-aspect upload: a file can be used as immediate chat context, ingested into the RAG corpus (Qdrant), or both. **Add to `config.py`:** -- `UPLOAD_DIR` — path for temporary upload storage, default `/tmp/jarvischat_uploads` +- `UPLOAD_DIR` — path for temporary upload storage, default `/tmp/caic_uploads` - `MAX_UPLOAD_BYTES` — max file size, default 20MB - `SUPPORTED_UPLOAD_TYPES` — set of MIME types: `text/plain`, `text/markdown`, `application/pdf`, `application/json`, `text/x-python`, `text/html` @@ -68,7 +68,7 @@ Behavior: - Validate MIME type against `SUPPORTED_UPLOAD_TYPES` — return 415 if unsupported - For PDF files, extract text using `pypdf` (add to requirements.txt) - For all other types, read as UTF-8 text -- If mode includes `ingest`: chunk the extracted text into 512-token overlapping chunks (128-token overlap), generate embeddings via `EMBED_URL` (http://192.168.50.108:11434/api/embeddings, model mxbai-embed-large), upsert into Qdrant collection `jarvischat` with metadata `{source: filename, upload_date: iso_timestamp, type: "upload"}` +- If mode includes `ingest`: chunk the extracted text into 512-token overlapping chunks (128-token overlap), generate embeddings via `EMBED_URL` (http://192.168.50.108:11434/api/embeddings, model mxbai-embed-large), upsert into Qdrant collection `caic` with metadata `{source: filename, upload_date: iso_timestamp, type: "upload"}` - If mode includes `context`: store the full extracted text in a new SQLite table `upload_context` with columns `(id INTEGER PRIMARY KEY, conversation_id TEXT, filename TEXT, content TEXT, created_at TEXT, expires_at TEXT)`. Context entries expire after 1 hour. - Return JSON: `{filename, size_bytes, mode, chunks_ingested (if ingest), context_id (if context), message}` @@ -120,7 +120,7 @@ Run full test suite. All existing tests must continue to pass. ## ~~TASK 6 — Roadmap I: Terminal Command RAG Hook [DONE]~~ -**Status: `POST /api/ingest` with Bearer token auth, `chunk_text()` shared helper, `jc-ingest.sh` script. Committed `1ac21ad` (v0.11.0).** +**Status: `POST /api/ingest` with Bearer token auth, `chunk_text()` shared helper, `caic-ingest.sh` script. Committed `1ac21ad` (v0.11.0).** This task implements autonomous RAG ingestion of significant terminal activity (TODO #23). @@ -134,21 +134,21 @@ Implement `POST /api/ingest` (requires Bearer token auth — use same `COMPLETIO Behavior: - Chunk `content` into 512-token overlapping chunks (128-token overlap) — extract this logic into a shared helper `chunk_text(text, chunk_size=512, overlap=128)` in `rag.py` if not already present - Generate embeddings via `EMBED_URL` -- Upsert into Qdrant collection `jarvischat` with metadata `{source, ingest_date: iso_timestamp, ...metadata}` +- Upsert into Qdrant collection `caic` with metadata `{source, ingest_date: iso_timestamp, ...metadata}` - Return JSON: `{chunks_ingested, source, message}` **Wire `ingest.router` into `app.py`.** -**Create `/home/gramps/bin/jc-ingest.sh` on jarvis (192.168.50.210)** — this is a shell script, not a Python file, and lives outside the repo. Write it to stdout/document it clearly so gramps can deploy it manually: +**Create `/usr/local/bin/caic-ingest.sh` on worker (192.168.50.210)** — this is a shell script, not a Python file, and lives outside the repo. Write it to stdout/document it clearly so gramps can deploy it manually: ```bash #!/bin/bash -# jc-ingest.sh — pipe terminal commands into jarvisChat RAG +# caic-ingest.sh — pipe terminal commands into cAIc RAG # Add to ~/.bashrc: export PROMPT_COMMAND="jc_capture" # Function to call after significant commands JC_URL="http://192.168.50.210:8080/api/ingest" -JC_TOKEN="${JARVISCHAT_COMPLETIONS_API_KEY}" +JC_TOKEN="${CAIC_COMPLETIONS_API_KEY}" jc_capture() { local cmd @@ -215,7 +215,7 @@ Run full test suite. All existing tests must continue to pass. ## ~~TASK 8 — Roadmap K: RAG Corpus Management [DONE]~~ -Qdrant collection `jarvischat` currently grows without bound. Implement score-based eviction with hysteresis, pinned sources, operational stats, and a flush command. +Qdrant collection `caic` currently grows without bound. Implement score-based eviction with hysteresis, pinned sources, operational stats, and a flush command. ### Config — add to `config.py`: @@ -242,7 +242,7 @@ Lower score = evicted first. Tiebreak: `last_accessed` ASC (older wins). ```python async def get_collection_count() -> int - # GET /collections/jarvischat → return vectors_count + # GET /collections/caic → return vectors_count async def get_collection_stats() -> dict # Return {vector_count, max_vectors, high_water, low_water, percent_full, pinned_sources} @@ -298,7 +298,7 @@ Call `maybe_evict()` after each upsert batch completes in: | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/api/rag/stats` | Operational stats (see `get_rag_operational_stats()`) — admin required | -| POST | `/api/rag/flush` | Delete ALL points from the Qdrant `jarvischat` collection. Returns `{deleted_count, collection: "jarvischat", status: "flushed"}`. Admin required. | +| POST | `/api/rag/flush` | Delete ALL points from the Qdrant `caic` collection. Returns `{deleted_count, collection: "caic", status: "flushed"}`. Admin required. | ### In-memory eviction log: @@ -325,18 +325,18 @@ Run full test suite. All existing tests must continue to pass. --- -## ~~TASK 9 — Roadmap N1: RabbitMQ Install and Service on Ultron (Infrastructure) [DONE]~~ +## ~~TASK 9 — Roadmap N1: RabbitMQ Install and Service on Coordinator (Infrastructure) [DONE]~~ -This task runs on ultron (this machine). Install RabbitMQ and verify it is operational. +This task runs on coordinator (this machine). Install RabbitMQ and verify it is operational. Run the following steps: 1. `apt-get update && apt-get install -y rabbitmq-server` 2. `systemctl enable rabbitmq-server && systemctl start rabbitmq-server` 3. `systemctl status rabbitmq-server` — verify active/running 4. Enable the management plugin: `rabbitmq-plugins enable rabbitmq_management` -5. Create a dedicated jC vhost: `rabbitmqctl add_vhost jarvischat` -6. Create a dedicated user: `rabbitmqctl add_user jarvischat CHANGEME_PASSWORD` — generate a random 24-char alphanumeric password and record it -7. Grant permissions: `rabbitmqctl set_permissions -p jarvischat jarvischat ".*" ".*" ".*"` +5. Create a dedicated jC vhost: `rabbitmqctl add_vhost caic` +6. Create a dedicated user: `rabbitmqctl add_user caic CHANGEME_PASSWORD` — generate a random 24-char alphanumeric password and record it +7. Grant permissions: `rabbitmqctl set_permissions -p caic caic ".*" ".*" ".*"` 8. Verify management UI is reachable: `curl -s -u guest:guest http://localhost:15672/api/overview | python3 -m json.tool` 9. Delete default guest user: `rabbitmqctl delete_user guest` @@ -344,7 +344,7 @@ Declare the two topic exchanges needed by jC: - Exchange name: `jc.admin`, type: `topic`, durable: true - Exchange name: `jc.system`, type: `topic`, durable: true -Use `rabbitmqadmin` or `curl` against the management API to declare exchanges. Verify both exchanges appear in: `curl -s -u jarvischat:{password} http://localhost:15672/api/exchanges/jarvischat` +Use `rabbitmqadmin` or `curl` against the management API to declare exchanges. Verify both exchanges appear in: `curl -s -u caic:{password} http://localhost:15672/api/exchanges/caic` Write the generated RabbitMQ password to `/home/gramps/.jc_amqp_secret` with mode 600. This will be read by jC as an env var source in subsequent tasks. @@ -354,12 +354,12 @@ No pytest tests required for this infrastructure task. ## ~~TASK 10 — Roadmap N2: AMQP Connection Layer in jC [DONE]~~ -This task adds the core AMQP connection manager to jC. It must connect to RabbitMQ on ultron (localhost from jC's perspective since jC runs on ultron), handle reconnection, and provide a shared channel for all AMQP operations. +This task adds the core AMQP connection manager to jC. It must connect to RabbitMQ on coordinator (localhost from jC's perspective since jC runs on coordinator), handle reconnection, and provide a shared channel for all AMQP operations. **Add to `requirements.txt`:** `aio-pika>=9.0.0` **Add to `config.py`:** -- `AMQP_URL` — read from env `JARVISCHAT_AMQP_URL`, default `amqp://jarvischat:password@localhost:5672/jarvischat`. The actual password comes from `/home/gramps/.jc_amqp_secret` — read it at startup if the env var is not set. +- `AMQP_URL` — read from env `CAIC_AMQP_URL`, default `amqp://caic:password@localhost:5672/caic`. The actual password comes from `/home/gramps/.jc_amqp_secret` — read it at startup if the env var is not set. - `AMQP_RECONNECT_DELAY` — seconds between reconnect attempts, default 5 - `AMQP_EXCHANGE_ADMIN` — `jc.admin` - `AMQP_EXCHANGE_SYSTEM` — `jc.system` @@ -423,7 +423,7 @@ Worker presence is assumed from registration onward. No periodic heartbeats — **register** (worker → coordinator): ```json { - "node_name": "jarvis", + "node_name": "worker01", "node_type": "worker", "ip": "192.168.50.210", "capabilities": { @@ -432,12 +432,12 @@ Worker presence is assumed from registration onward. No periodic heartbeats — }, "active_model": { "name": "llama3.1", "version": "latest", "quant": "Q4_K_M", - "path": "/home/jarvis/models/llama3.1-latest-Q4_K_M.gguf", + "path": "/var/lib/caic/models/llama3.1-latest-Q4_K_M.gguf", "port": 8081 }, "inventory": [ {"name": "llama3.1", "version": "latest", "quant": "Q4_K_M", - "path": "/home/jarvis/models/llama3.1-latest-Q4_K_M.gguf", "port": 8081} + "path": "/var/lib/caic/models/llama3.1-latest-Q4_K_M.gguf", "port": 8081} ], "status": "active" } @@ -446,7 +446,7 @@ Worker presence is assumed from registration onward. No periodic heartbeats — **deregister** (worker → coordinator): ```json { - "node_name": "jarvis", + "node_name": "worker01", "reason": "shutdown", "timestamp": "2026-07-06T12:00:00Z" } @@ -455,8 +455,8 @@ Worker presence is assumed from registration onward. No periodic heartbeats — **ping** (coordinator → worker): ```json { - "from": "ultron", - "node_name": "jarvis", + "from": "coordinator", + "node_name": "worker01", "type": "ping", "correlation_id": "550e8400-e29b-41d4-a716-446655440000", "timestamp": "2026-07-06T12:00:00Z" @@ -467,7 +467,7 @@ Worker must respond within 5 seconds or the coordinator considers it absent. **pong** (worker → coordinator): ```json { - "node_name": "jarvis", + "node_name": "worker01", "type": "pong", "correlation_id": "550e8400-e29b-41d4-a716-446655440000", "status": "active", @@ -485,8 +485,8 @@ Correlation ID matches the ping so the coordinator can pair request and response Coordinator responds on `cluster.coordinator.response`: ```json { - "coordinator_node": "ultron", - "cluster_nodes": ["jarvis"], + "coordinator_node": "coordinator", + "cluster_nodes": ["worker01"], "timestamp": "2026-07-06T12:00:00Z" } ``` @@ -494,7 +494,7 @@ Coordinator responds on `cluster.coordinator.response`: **event** (worker → coordinator): ```json { - "node_name": "jarvis", + "node_name": "worker01", "severity": "info", "message": "llama-server started with model llama3.1:latest", "details": {"model": "llama3.1:latest", "port": 8081, "pid": 1234}, @@ -667,22 +667,22 @@ Run full test suite. All existing tests must continue to pass. --- -## TASK 12 — Roadmap N4: Worker Node Registration Publisher (Jarvis Side) +## TASK 12 — Roadmap N4: Worker Node Registration Publisher (Worker Side) -This task creates the worker node AMQP client that runs on jarvis (192.168.50.210). It is a standalone Python script — not part of the jC FastAPI app — that runs as a systemd service on jarvis. +This task creates the worker node AMQP client that runs on worker (192.168.50.210). It is a standalone Python script — not part of the jC FastAPI app — that runs as a systemd service on worker. **Create `node_agent/agent.py`** in the repo (new directory). ### 12.1 Config & Inventory Discovery -On start, reads `/etc/jc-node-agent.conf` (INI format): +On start, reads `/etc/caic-node-agent.conf` (INI format): - `node_name` — hostname, default from `socket.gethostname()` - `node_ip` — LAN IP, default from socket - `node_type` — `"worker"` (fixed) - `capabilities` — comma-separated list, e.g. `llm,rag` -- `amqp_url` — RabbitMQ URL on ultron, e.g. `amqp://jarvischat:password@192.168.50.108:5672/jarvischat` +- `amqp_url` — RabbitMQ URL on coordinator, e.g. `amqp://caic:password@192.168.50.108:5672/caic` - `llama_port` — port llama-server/llama-rpc is listening on, default 8081 -- `models_dir` — path to GGUF model files, default `/home/gramps/models` +- `models_dir` — path to GGUF model files, default `/var/lib/caic/models` - `active_model` — filename of currently active model (without path) Discovers inventory by globbing `models_dir` for `*.gguf` files and parsing name/version/quant from filename using regex pattern: `{name}-{version}-{quant}.gguf` where quant matches `Q[0-9]+_K_[A-Z]+` or similar standard suffixes. @@ -692,7 +692,7 @@ Discovers inventory by globbing `models_dir` for `*.gguf` files and parsing name Publishes registration to `jc.admin`, routing key `node.{node_name}.register`: ```json { - "node_name": "jarvis", + "node_name": "worker01", "node_type": "worker", "ip": "192.168.50.210", "capabilities": ["llm"], @@ -710,7 +710,7 @@ After admission: listens on `jc.admin`, routing key `node.{node_name}.ping`. On ```json { - "node_name": "jarvis", + "node_name": "worker01", "type": "pong", "correlation_id": "", "status": "active", @@ -727,7 +727,7 @@ No periodic heartbeats. Worker sits idle between pings — coordinator only ping Listens on `jc.admin`, routing key `node.{node_name}.cmd.swap_model`: - Payload: `{model_filename: str}` - Stops current llama-server: `systemctl stop llama-server` -- Updates `/etc/jc-node-agent.conf` active_model field +- Updates `/etc/caic-node-agent.conf` active_model field - Starts llama-server: `systemctl start llama-server` (assumes service reads active_model from conf or ExecStart is updated) - Waits for llama-server to be healthy: poll `http://localhost:{llama_port}/v1/models` every 2s, timeout 120s - Publishes to `jc.system`, routing key `node.{node_name}.model_ready`: @@ -740,7 +740,7 @@ Listens on `jc.admin`, routing key `node.{node_name}.cmd.swap_model`: **Create `node_agent/requirements.txt`:** `aio-pika>=9.0.0` -**Document `/etc/jc-node-agent.conf` format** in a comment block at the top of `agent.py`. +**Document `/etc/caic-node-agent.conf` format** in a comment block at the top of `agent.py`. **Write `tests/test_node_agent.py`** covering: - Registration payload construction from config + model discovery — assert correct JSON shape @@ -763,9 +763,9 @@ This task wires the cluster into jC's chat flow. When a query arrives at `/api/c **Prerequisites:** Tasks 9–12 complete. At least one worker node admitted to cluster. -**Install Phi-4-mini on ultron (infrastructure step):** -- Download `Phi-4-mini-Instruct-Q4_K_M.gguf` from HuggingFace using `hf download microsoft/Phi-4-mini-instruct --include "*.Q4_K_M.gguf" --local-dir /home/gramps/models` -- Create `/etc/systemd/system/llama-server-triage.service` — same pattern as existing llama-server service but: port 8083, model path points to Phi-4-mini GGUF, no `--rpc` flag (runs entirely on ultron CPU/iGPU), description `Llama.cpp Server (Phi-4-mini — triage/routing)` +**Install Phi-4-mini on coordinator (infrastructure step):** +- Download `Phi-4-mini-Instruct-Q4_K_M.gguf` from HuggingFace using `hf download microsoft/Phi-4-mini-instruct --include "*.Q4_K_M.gguf" --local-dir /var/lib/jc/models` +- Create `/etc/systemd/system/llama-server-triage.service` — same pattern as existing llama-server service but: port 8083, model path points to Phi-4-mini GGUF, no `--rpc` flag (runs entirely on coordinator CPU/iGPU), description `Llama.cpp Server (Phi-4-mini — triage/routing)` - `systemctl daemon-reload && systemctl enable llama-server-triage && systemctl start llama-server-triage` - Verify: `curl -s http://localhost:8083/v1/models` @@ -820,7 +820,7 @@ Run full test suite. All existing tests must continue to pass. ## TASK 14 — Roadmap N6: Model Swap Command Flow -This task implements the ultron-side logic for requesting a model swap on a worker node when the ideal model is not currently active. +This task implements the coordinator-side logic for requesting a model swap on a worker node when the ideal model is not currently active. **Add to `cluster.py`:** @@ -925,10 +925,10 @@ Commit all changes introduced across Tasks 9–15 with message: `feat: Roadmap N ### B3 — Docker distribution (v1.0 gate) -**Goal:** Ship jarvisChat as a `docker compose` stack so a single command stands up everything. +**Goal:** Ship cAIc as a `docker compose` stack so a single command stands up everything. **Services to containerize:** -- jarvisChat (FastAPI app + SQLite) +- cAIc (FastAPI app + SQLite) - SearXNG - Qdrant - RabbitMQ @@ -936,7 +936,7 @@ Commit all changes introduced across Tasks 9–15 with message: `feat: Roadmap N - Ollama (embeddings) **Also needed:** -- `Dockerfile` for the jarvisChat app itself +- `Dockerfile` for the cAIc app itself - `docker-compose.yml` with all services, volumes, networks, env vars - Setup wizard script (run on first boot) that: - Probes CPU vs GPU (reuses `hardware.py`) diff --git a/amqp.py b/amqp.py index 64a8457..3a307a7 100644 --- a/amqp.py +++ b/amqp.py @@ -1,5 +1,5 @@ """ -JarvisChat — AMQP connection manager. +cAIc — AMQP connection manager. Single persistent aio-pika connection with auto-reconnect. """ import asyncio @@ -21,7 +21,7 @@ from config import ( get_amqp_url, ) -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") _connection = None _channel = None diff --git a/app.py b/app.py index ae6492b..1baa3cd 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -JarvisChat - Entry point. +cAIc - Entry point. Creates the FastAPI app, registers middleware, mounts all routers. """ import logging @@ -43,10 +43,10 @@ import routers.rag_admin as rag_admin import routers.cluster as cluster_router # --- Logging --- -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") log.setLevel(logging.DEBUG) syslog_handler = logging.handlers.SysLogHandler(address="/dev/log") -syslog_handler.setFormatter(logging.Formatter("jarvischat[%(process)d]: %(levelname)s %(message)s")) +syslog_handler.setFormatter(logging.Formatter("caic[%(process)d]: %(levelname)s %(message)s")) log.addHandler(syslog_handler) BASE_DIR = Path(__file__).parent @@ -55,7 +55,7 @@ templates = Jinja2Templates(directory=str(BASE_DIR / "templates")) @asynccontextmanager async def lifespan(app: FastAPI): - log.info(f"JarvisChat {VERSION} starting up") + log.info(f"cAIc {VERSION} starting up") os.makedirs(UPLOAD_DIR, exist_ok=True) init_db() log.info(f"Memory system: {get_memory_count()} memories loaded") @@ -75,11 +75,11 @@ async def lifespan(app: FastAPI): log.warning("RAG_MAX_VECTORS <= 0 — RAG eviction disabled") yield - log.info("JarvisChat shutting down") + log.info("cAIc shutting down") await amqp_disconnect() -app = FastAPI(title="JarvisChat", lifespan=lifespan) +app = FastAPI(title="cAIc", lifespan=lifespan) @app.exception_handler(Exception) diff --git a/auth.py b/auth.py index 8a8cafb..cf8464c 100644 --- a/auth.py +++ b/auth.py @@ -1,5 +1,5 @@ """ -JarvisChat - Auth: session management, PIN verification, middleware, auth routes. +cAIc - Auth: session management, PIN verification, middleware, auth routes. """ import hashlib import hmac @@ -21,7 +21,7 @@ from security import ( read_json_body, hash_pin, customer_error_envelope, log_incident, ) -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") router = APIRouter() diff --git a/cluster.py b/cluster.py index ee863a6..deac9a8 100644 --- a/cluster.py +++ b/cluster.py @@ -1,5 +1,5 @@ """ -JarvisChat — Cluster protocol implementation. +cAIc — Cluster protocol implementation. Maintains node registry, event log, coordinator state, and ping-based health checks. """ import asyncio @@ -11,7 +11,7 @@ from datetime import datetime, timezone from amqp import publish, subscribe from config import AMQP_EXCHANGE_ADMIN, AMQP_EXCHANGE_SYSTEM -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") CLUSTER_NODES: dict[str, dict] = {} CLUSTER_EVENTS: deque = deque(maxlen=1000) diff --git a/config.py b/config.py index 4c15341..aff95d0 100644 --- a/config.py +++ b/config.py @@ -1,5 +1,5 @@ """ -JarvisChat - Central configuration. +cAIc - Central configuration. All constants, environment variables, limits, and skill registry live here. """ import os @@ -7,23 +7,23 @@ import re import ipaddress import logging -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") VERSION = "v0.14.0" OLLAMA_BASE = os.environ.get("OLLAMA_BASE", "http://localhost:11434") LLAMA_SERVER_BASE = os.environ.get("LLAMA_SERVER_BASE", "http://192.168.50.108:8081") SEARXNG_BASE = "http://localhost:8888" DEFAULT_MODEL = "llama3.1:latest" -COMPLETIONS_API_KEY = os.environ.get("JARVISCHAT_COMPLETIONS_API_KEY", "jc-sk-" + os.urandom(24).hex()) +COMPLETIONS_API_KEY = os.environ.get("CAIC_COMPLETIONS_API_KEY", "caic-sk-" + os.urandom(24).hex()) # --- AMQP --- AMQP_RECONNECT_DELAY = 5 AMQP_EXCHANGE_ADMIN = "jc.admin" AMQP_EXCHANGE_SYSTEM = "jc.system" -AMQP_SECRET_PATH = "/home/gramps/.jc_amqp_secret" +AMQP_SECRET_PATH = "/home/gramps/.caic_amqp_secret" def get_amqp_url() -> str: - url = os.environ.get("JARVISCHAT_AMQP_URL") + url = os.environ.get("CAIC_AMQP_URL") if url: return url try: @@ -31,22 +31,22 @@ def get_amqp_url() -> str: pw = f.read().strip() except (FileNotFoundError, OSError): pw = "password" - return f"amqp://jarvischat:{pw}@localhost:5672/jarvischat" + return f"amqp://caic:{pw}@localhost:5672/caic" # --- Auth --- SESSION_TIMEOUT_SECONDS = 90 MAX_PIN_ATTEMPTS = 5 PIN_LOCKOUT_SECONDS = 300 -ALLOW_DEFAULT_PIN = os.getenv("JARVISCHAT_ALLOW_DEFAULT_PIN", "false").lower() == "true" +ALLOW_DEFAULT_PIN = os.getenv("CAIC_ALLOW_DEFAULT_PIN", "false").lower() == "true" TRUSTED_ORIGINS = { origin.strip().rstrip("/") - for origin in os.getenv("JARVISCHAT_TRUSTED_ORIGINS", "").split(",") + for origin in os.getenv("CAIC_TRUSTED_ORIGINS", "").split(",") if origin.strip() } DEFAULT_ALLOWED_CIDRS = "127.0.0.0/8,::1/128,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" -ALLOWED_CIDRS_RAW = os.getenv("JARVISCHAT_ALLOWED_CIDRS", DEFAULT_ALLOWED_CIDRS) +ALLOWED_CIDRS_RAW = os.getenv("CAIC_ALLOWED_CIDRS", DEFAULT_ALLOWED_CIDRS) TRUST_X_FORWARDED_FOR = ( - os.getenv("JARVISCHAT_TRUST_X_FORWARDED_FOR", "false").lower() == "true" + os.getenv("CAIC_TRUST_X_FORWARDED_FOR", "false").lower() == "true" ) # --- Rate limits --- @@ -64,11 +64,11 @@ BODY_LIMIT_CHAT_BYTES = 128 * 1024 BODY_LIMIT_PROFILE_BYTES = 256 * 1024 # --- Upload --- -UPLOAD_DIR = "/tmp/jarvischat_uploads" +UPLOAD_DIR = "/tmp/caic_uploads" MAX_UPLOAD_BYTES = 20 * 1024 * 1024 SUPPORTED_UPLOAD_TYPES = {"text/plain", "text/markdown", "application/pdf", "application/json", "text/x-python", "text/html", "image/png", "image/jpeg", "image/gif", "image/svg+xml", "image/webp"} QDRANT_URL = "http://192.168.50.108:6333" -RAG_COLLECTION = "jarvis_rag" +RAG_COLLECTION = "caic_rag" UPLOAD_CONTEXT_EXPIRY_HOURS = 1 BODY_LIMIT_UPLOAD_BYTES = MAX_UPLOAD_BYTES diff --git a/db.py b/db.py index 60d0422..73a370a 100644 --- a/db.py +++ b/db.py @@ -1,5 +1,5 @@ """ -JarvisChat - Database layer. +cAIc - Database layer. Schema init, connection factory, settings helpers, skill state management. """ import logging @@ -16,10 +16,10 @@ from config import ( MAX_SKILL_PROMPT_CHARS, ALLOWED_NETWORKS, ) -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") BASE_DIR = Path(__file__).parent -DB_PATH = BASE_DIR / "jarvischat.db" +DB_PATH = BASE_DIR / "caic.db" def get_db(): @@ -190,15 +190,15 @@ def init_db(): existing_pin_salt = conn.execute("SELECT value FROM settings WHERE key = 'admin_pin_salt'").fetchone() if not existing_pin_hash or not existing_pin_salt: from config import ALLOW_DEFAULT_PIN - configured_pin = os.getenv("JARVISCHAT_ADMIN_PIN", "").strip() + configured_pin = os.getenv("CAIC_ADMIN_PIN", "").strip() if re.fullmatch(r"\d{4}", configured_pin): seed_pin, pin_source = configured_pin, "env" elif ALLOW_DEFAULT_PIN: seed_pin, pin_source = "1234", "default" else: raise RuntimeError( - "Admin PIN bootstrap blocked: set JARVISCHAT_ADMIN_PIN to a 4-digit PIN " - "or set JARVISCHAT_ALLOW_DEFAULT_PIN=true." + "Admin PIN bootstrap blocked: set CAIC_ADMIN_PIN to a 4-digit PIN " + "or set CAIC_ALLOW_DEFAULT_PIN=true." ) salt_hex, pin_hash_hex = hash_pin(seed_pin) conn.execute("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)", ("admin_pin_hash", pin_hash_hex)) diff --git a/docker.md b/docker.md index 2f01ea3..229f43b 100644 --- a/docker.md +++ b/docker.md @@ -1,6 +1,6 @@ # Docker Distribution — Architecture & Planning -> **Part of B3 (v1.0 gate).** This document catalogs every service, volume, port, configuration, and decision needed to ship jarvisChat as a `docker compose` stack. It also defines extraction (setup) and back-out (uninstall) procedures so nothing is lost when reality disagrees with the plan. +> **Part of B3 (v1.0 gate).** This document catalogs every service, volume, port, configuration, and decision needed to ship cAIc as a `docker compose` stack. It also defines extraction (setup) and back-out (uninstall) procedures so nothing is lost when reality disagrees with the plan. ## 1. Stack Overview @@ -15,10 +15,10 @@ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────────────────────────────────────────────┐ │ -│ │ jarvisChat (FastAPI) │ │ +│ │ cAIc (FastAPI) │ │ │ │ :8080 (HTTP) │ │ │ │ │ │ -│ │ SQLite ◄── jarvischat.db (volume) │ │ +│ │ SQLite ◄── caic.db (volume) │ │ │ │ Uploads ◄── /app/uploads (volume) │ │ │ └──────────┬──────────────┬───────────────────────┘ │ │ │ │ │ @@ -37,7 +37,7 @@ | Service | Image | Role | |---------|-------|------| -| **jarvisChat** | Custom `Dockerfile` | FastAPI app serving UI + API | +| **cAIc** | Custom `Dockerfile` | FastAPI app serving UI + API | | **SearXNG** | `searxng/searxng:latest` | Privacy-respecting web search | | **Qdrant** | `qdrant/qdrant:latest` | Vector database for RAG | | **RabbitMQ** | `rabbitmq:4-management` | Message broker for AMQP cluster | @@ -57,9 +57,9 @@ ## 2. Service Catalog -### 2.1 jarvisChat (FastAPI app) +### 2.1 cAIc (FastAPI app) -**Image:** `jarvischat:latest` (built from `Dockerfile`) +**Image:** `caic:latest` (built from `Dockerfile`) **Ports:** | Container | Host | Purpose | @@ -69,8 +69,8 @@ **Volumes:** | Container path | Type | Purpose | |----------------|------|---------| -| `/app/jarvischat.db` | named volume `jarvischat_data` | SQLite database | -| `/app/uploads` | named volume `jarvischat_uploads` | Uploaded files | +| `/app/caic.db` | named volume `caic_data` | SQLite database | +| `/app/uploads` | named volume `caic_uploads` | Uploaded files | | `/app/hardware_state.json` | (inside volume) | Cached hardware probe | **Dependencies:** Wait for SearXNG, Qdrant, RabbitMQ, llama-server, Ollama before serving. @@ -153,7 +153,7 @@ QDRANT__SERVICE__GRPC_PORT=6334 **Environment:** ```env -RABBITMQ_DEFAULT_USER=jarvischat +RABBITMQ_DEFAULT_USER=caic RABBITMQ_DEFAULT_PASS_FILE=/run/secrets/rabbitmq_password RABBITMQ_DEFAULT_VHOST=/ ``` @@ -227,9 +227,9 @@ LLAMA_ARG_RPC= # optional: comma-separated RPC endpoints ```env # --- Secrets (auto-generated, change before production) --- -JARVISCHAT_ADMIN_PIN= -JARVISCHAT_COMPLETIONS_API_KEY= -JARVISCHAT_ALLOW_DEFAULT_PIN=false +CAIC_ADMIN_PIN= +CAIC_COMPLETIONS_API_KEY= +CAIC_ALLOW_DEFAULT_PIN=false RABBITMQ_PASSWORD= SEARXNG_SECRET_KEY= @@ -257,9 +257,9 @@ LLAMA_CTX_SIZE=4096 OLLAMA_EMBED_MODEL=all-minilm:latest # --- Network --- -JARVISCHAT_ALLOWED_CIDRS=127.0.0.0/8,::1/128,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -JARVISCHAT_TRUSTED_ORIGINS= -JARVISCHAT_TRUST_X_FORWARDED_FOR=false +CAIC_ALLOWED_CIDRS=127.0.0.0/8,::1/128,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 +CAIC_TRUSTED_ORIGINS= +CAIC_TRUST_X_FORWARDED_FOR=false ``` ### 3.2 Mapping of config.py → .env variable @@ -272,24 +272,24 @@ Every config.py default that references an external service must accept a matchi | `OLLAMA_BASE` | `OLLAMA_BASE` | Ollama | | `SEARXNG_BASE` | `SEARXNG_BASE` | SearXNG | | `QDRANT_URL` | `QDRANT_URL` | Qdrant | -| `COMPLETIONS_API_KEY` | `JARVISCHAT_COMPLETIONS_API_KEY` | — | -| `ALLOWED_CIDRS_RAW` | `JARVISCHAT_ALLOWED_CIDRS` | — | -| `TRUST_X_FORWARDED_FOR` | `JARVISCHAT_TRUST_X_FORWARDED_FOR` | — | -| `TRUSTED_ORIGINS` | `JARVISCHAT_TRUSTED_ORIGINS` | — | +| `COMPLETIONS_API_KEY` | `CAIC_COMPLETIONS_API_KEY` | — | +| `ALLOWED_CIDRS_RAW` | `CAIC_ALLOWED_CIDRS` | — | +| `TRUST_X_FORWARDED_FOR` | `CAIC_TRUST_X_FORWARDED_FOR` | — | +| `TRUSTED_ORIGINS` | `CAIC_TRUSTED_ORIGINS` | — | | `RAG_MAX_VECTORS` | `RAG_MAX_VECTORS` | — (calc'd from RAM) | ### 3.3 Secrets management | Secret | Generated by | Stored in | Mounted to | |--------|-------------|-----------|------------| -| `JARVISCHAT_ADMIN_PIN` | User prompt | `.env` | jarvisChat container | -| `JARVISCHAT_COMPLETIONS_API_KEY` | Auto-generated, shown to user | `.env` | jarvisChat container | +| `CAIC_ADMIN_PIN` | User prompt | `.env` | cAIc container | +| `CAIC_COMPLETIONS_API_KEY` | Auto-generated, shown to user | `.env` | cAIc container | | `RABBITMQ_PASSWORD` | Auto-generated | `.env` + Docker secret | RabbitMQ container | | `SEARXNG_SECRET_KEY` | Auto-generated | `.env` | SearXNG container | **Docker secrets approach:** Use `secrets:` in compose file for RabbitMQ password (mounted as file) rather than passing via env var, since `settings.yml` in SearXNG and RabbitMQ config can reference file-based secrets without env-var leakage. -### 3.4 Dockerfile for jarvisChat +### 3.4 Dockerfile for cAIc ```dockerfile FROM python:3.13-slim-bookworm AS builder @@ -321,12 +321,12 @@ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"] ```yaml services: - jarvischat: + caic: build: . ports: ["8080:8080"] volumes: - - jarvischat_data:/app/jarvischat.db - - jarvischat_uploads:/app/uploads + - caic_data:/app/caic.db + - caic_uploads:/app/uploads env_file: .env depends_on: searxng: { condition: service_started } @@ -416,8 +416,8 @@ services: restart: unless-stopped volumes: - jarvischat_data: - jarvischat_uploads: + caic_data: + caic_uploads: searxng_config: qdrant_storage: rabbitmq_data: @@ -441,11 +441,11 @@ secrets: | From | To | Port | Protocol | |------|----|------|----------| -| jarvisChat | llama-server | 8081 | HTTP | -| jarvisChat | Ollama | 11434 | HTTP | -| jarvisChat | SearXNG | 8080 | HTTP | -| jarvisChat | Qdrant | 6333 | HTTP | -| jarvisChat | RabbitMQ | 5672 | AMQP | +| cAIc | llama-server | 8081 | HTTP | +| cAIc | Ollama | 11434 | HTTP | +| cAIc | SearXNG | 8080 | HTTP | +| cAIc | Qdrant | 6333 | HTTP | +| cAIc | RabbitMQ | 5672 | AMQP | | RabbitMQ | (cluster peers) | 4369 | EPMD | | RabbitMQ | (cluster peers) | 25672 | Inter-node | @@ -453,7 +453,7 @@ secrets: | Port | Service | Should expose? | Notes | |------|---------|---------------|-------| -| 8080 | jarvisChat | ✅ Required | UI + API | +| 8080 | cAIc | ✅ Required | UI + API | | 8888 | SearXNG | Optional | Only if user wants standalone search | | 6333 | Qdrant | Optional | Only for external tooling | | 5672 | RabbitMQ | Optional | Only for remote AMQP clients | @@ -461,7 +461,7 @@ secrets: | 8081 | llama-server | Optional | Only for external tooling | | 11434 | Ollama | Optional | Only for external tooling | -**Design decision:** By default, only port 8080 (jarvisChat) is published. All other services remain on the internal compose network. Advanced users can opt-in by uncommenting `ports:` blocks. +**Design decision:** By default, only port 8080 (cAIc) is published. All other services remain on the internal compose network. Advanced users can opt-in by uncommenting `ports:` blocks. ### 5.3 Reverse proxy consideration @@ -534,7 +534,7 @@ This is out of scope for v1.0 but documented for future. ./docker-deploy/ ├── .env # All env vars (SECRET — add to .gitignore) ├── docker-compose.yml # Compose stack definition -├── Dockerfile # jarvisChat image build +├── Dockerfile # cAIc image build ├── secrets/ │ └── rabbitmq_password.txt # RabbitMQ password file ├── searxng/ @@ -564,9 +564,9 @@ Re-running `setup.sh`: | Item | Removal method | |------|---------------| | Docker containers | `docker compose down -v` | -| Docker images | `docker rmi jarvischat:latest` (ask about other images) | -| Docker volumes | `docker volume rm jarvischat_data ...` (prompt first) | -| Network `jarvischat_default` | Removed with compose | +| Docker images | `docker rmi caic:latest` (ask about other images) | +| Docker volumes | `docker volume rm caic_data ...` (prompt first) | +| Network `caic_default` | Removed with compose | | `.env` file | `rm .env` | | `secrets/` directory | `rm -rf secrets/` | | `searxng/` directory | `rm -rf searxng/` | @@ -578,7 +578,7 @@ Re-running `setup.sh`: | Item | Reason | |------|--------| | `./models/*.gguf` | User data — prompt for deletion | -| `jarvischat.db` (in volume) | Prompt: "Keep database snapshot?" | +| `caic.db` (in volume) | Prompt: "Keep database snapshot?" | | `./uploads/` (in volume) | Prompt: "Keep uploaded files?" | | Docker Engine itself | Not installed by this project — leave it | @@ -596,12 +596,12 @@ Re-running `setup.sh`: 3. ASK: "Remove secrets/ and searxng/ directories?" (default no) 4. ASK: "Remove Docker images? (y/N)" (default no) - ├── Y → docker rmi jarvischat:latest + ├── Y → docker rmi caic:latest ├── Y → docker image ls | grep searxng/qdrant/rabbitmq → prompt per image └── N → skip 5. ASK: "Keep database volume snapshot? (Y/n)" (default yes) - ├── N → docker volume rm jarvischat_data + ├── N → docker volume rm caic_data └── Y → leave volume (can be reattached later) 6. ASK: "Remove model files from ./models/? (y/N)" (default no) @@ -653,7 +653,7 @@ The Docker stack above defines the **coordinator** only. Workers (headless infer ### 9.1 What a worker runs ``` -Worker machine (e.g. jarvis, Corsair) +Worker machine (e.g. worker01, worker02) ┌────────────────────────────────────┐ │ llama-server │ │ (single binary, no build needed) │ @@ -717,7 +717,7 @@ The broker-mediated model is the preferred architecture for this project because - [ ] `Dockerfile` written and builds clean - [ ] `docker-compose.yml` boots all containers -- [ ] jarvisChat container reaches all services (env vars resolve correctly) +- [ ] cAIc container reaches all services (env vars resolve correctly) - [ ] SearXNG settings.yml generated correctly by setup.sh - [ ] RabbitMQ password secret mounted correctly - [ ] GPU (NVIDIA) passes through to llama-server container @@ -736,7 +736,7 @@ The broker-mediated model is the preferred architecture for this project because ``` docker.md ← this file (planning doc) -Dockerfile ← jarvisChat image +Dockerfile ← cAIc image docker-compose.yml ← full stack .env.example ← template without secrets setup.sh ← extraction wizard diff --git a/docs/wiki/Developer-Architecture.md b/docs/wiki/Developer-Architecture.md index 1614dbd..81da708 100644 --- a/docs/wiki/Developer-Architecture.md +++ b/docs/wiki/Developer-Architecture.md @@ -1,10 +1,10 @@ # Developer Architecture Guide -This document explains how JarvisChat is structured, the external services it integrates with, and the key architectural changes made during development. +This document explains how cAIc is structured, the external services it integrates with, and the key architectural changes made during development. ## 1. System Overview -JarvisChat is a single-process FastAPI service with a Jinja2 frontend and SQLite persistence. It connects to an external llama-server for inference and optionally to SearXNG (web search), Qdrant (vector search), and RabbitMQ (AMQP cluster messaging). +cAIc is a single-process FastAPI service with a Jinja2 frontend and SQLite persistence. It connects to an external llama-server for inference and optionally to SearXNG (web search), Qdrant (vector search), and RabbitMQ (AMQP cluster messaging). ### 1.1 Module Layout @@ -29,11 +29,11 @@ Refactored from single-file (`app.py`) into modules under project root: | Service | Required | Port | Purpose | |---------|----------|------|---------| -| llama-server (ultron) | Yes | 8081 | LLM inference (OpenAI-compat), RPC offload to jarvis:50052 | +| llama-server (coordinator) | Yes | 8081 | LLM inference (OpenAI-compat), RPC offload to worker:50052 | | SearXNG | No | 8888 | Privacy-respecting web search | -| Qdrant (ultron) | No | 6333 | Vector database for RAG | -| Ollama (jarvis) | No | 11434 | Embeddings for RAG chunk vectors | -| RabbitMQ (ultron) | No | 5672 | AMQP broker for cluster messaging | +| Qdrant (coordinator) | No | 6333 | Vector database for RAG | +| Ollama (worker) | No | 11434 | Embeddings for RAG chunk vectors | +| RabbitMQ (coordinator) | No | 5672 | AMQP broker for cluster messaging | | rocm-smi | No | — | AMD GPU stats (host-level) | ### 1.3 Config Discovery @@ -42,11 +42,11 @@ Key base URLs are configured via environment variables with sensible defaults: | Variable | Default | Service | |----------|---------|---------| -| `LLAMA_SERVER_BASE` | `http://192.168.50.108:8081` | llama-server on ultron | +| `LLAMA_SERVER_BASE` | `http://192.168.50.108:8081` | llama-server on coordinator | | `OLLAMA_BASE` | `http://localhost:11434` | Legacy — all inference goes through LLAMA_SERVER_BASE | | `SEARXNG_BASE` | `http://localhost:8888` | SearXNG | -| `QDRANT_URL` | `http://192.168.50.108:6333` | Qdrant on ultron | -| `JARVISCHAT_AMQP_URL` | `amqp://jarvischat:password@localhost:5672/jarvischat` | RabbitMQ | +| `QDRANT_URL` | `http://192.168.50.108:6333` | Qdrant on coordinator | +| `CAIC_AMQP_URL` | `amqp://caic:password@localhost:5672/caic` | RabbitMQ | ## 2. Request/Response Architecture @@ -73,7 +73,7 @@ Key base URLs are configured via environment variables with sensible defaults: 1. Bearer token auth (same key as completions API) 2. Chunk text via shared `chunk_text()` helper (512-token chunks, 128-token overlap) 3. Embed via Ollama `/api/embeddings` -4. Upsert to Qdrant collection `jarvis_rag` +4. Upsert to Qdrant collection `caic_rag` 5. Trigger `maybe_evict()` if collection exceeds high-water mark ### 2.4 Upload Pipeline (`/api/upload`) @@ -116,7 +116,7 @@ Design notes: - Admin PIN hashed with PBKDF2-HMAC-SHA256 + salt - Failed PIN attempts tracked per client IP (max 5, 300s lockout) -- Default PIN allowed only if JARVISCHAT_ALLOW_DEFAULT_PIN=true +- Default PIN allowed only if CAIC_ALLOW_DEFAULT_PIN=true ### 4.3 Browser and API Abuse Controls @@ -141,8 +141,8 @@ Design notes: ### 5.1 Vector Search -- Qdrant collection `jarvis_rag` on ultron:6333 -- Embeddings via Ollama on jarvis:11434 (`/api/embeddings`) +- Qdrant collection `caic_rag` on coordinator:6333 +- Embeddings via Ollama on worker:11434 (`/api/embeddings`) - Shared `chunk_text(text, chunk_size=512, overlap=128)` helper in rag.py - Upload and ingest endpoints share the same chunk+embed+upsert pipeline @@ -178,7 +178,7 @@ Eviction module at `eviction.py` (re-exported through `rag.py` for backward comp ### 6.1 Design Model: Broker-Mediated -JarvisChat uses a **broker-mediated** cluster design. This is the preferred architecture and is reflected in all implementation decisions below. +cAIc uses a **broker-mediated** cluster design. This is the preferred architecture and is reflected in all implementation decisions below. **How it works:** - A single RabbitMQ broker (or clustered set of brokers) acts as the central nervous system @@ -208,7 +208,7 @@ Every physical machine in the cluster is classified by which services it runs. T | **RabbitMQ server** | Required — hosts the broker | Not required — connects as AMQP client only | | **RabbitMQ client (aio-pika)** | Required — publishes commands, consumes events | Required — consumes commands, publishes events | | **FastAPI / uvicorn** | Required | Not needed | -| **SQLite** | Required — owns jarvischat.db | Not needed | +| **SQLite** | Required — owns caic.db | Not needed | | **Qdrant** | Optional (recommended) — vector DB for RAG | Not needed | | **SearXNG** | Optional — web search | Not needed | | **llama-server** | Optional — can share its own GPU for inference | Required — this is why the worker exists | @@ -220,7 +220,7 @@ Every physical machine in the cluster is classified by which services it runs. T ``` Coordinator Worker(s) ┌────────────────────┐ ┌─────────────────────────┐ -│ jarvisChat │ │ llama-server │ +│ cAIc │ │ llama-server │ │ (FastAPI + SQLite)│ │ (inference) │ │ RabbitMQ server │◄──AMQP───────│ aio-pika (agent) │ │ SearXNG (opt) │ persistent │ ROCm / CUDA (if GPU) │ @@ -243,7 +243,7 @@ Every RabbitMQ server belongs to a cluster. Currently only the coordinator runs Pending implementation (Tasks 10–15): - `amqp.py` — aio-pika connection manager with reconnect -- Node agent on jarvis — registration, heartbeat, command consumer +- Node agent on worker — registration, heartbeat, command consumer - `triage.py` — Phi-4-mini query classification (general/code/search/rag) - Dynamic model swap via llama-server RPC diff --git a/docs/wiki/current-wip.md b/docs/wiki/current-wip.md index 615be41..a940ab3 100644 --- a/docs/wiki/current-wip.md +++ b/docs/wiki/current-wip.md @@ -1,4 +1,4 @@ -# JarvisChat Current WiP Backlog +# cAIc Current WiP Backlog Last updated: 2026-07-06 Owner: Gramps @@ -9,9 +9,9 @@ Scope: Active roadmap items and backlog. | Task | Description | Status | |------|-------------|--------| | Task 8 | RAG Corpus Management (score-based eviction, pinned sources, operational stats) | **DONE** | -| Task 9 | RabbitMQ install + exchange setup on ultron | **DONE** | +| Task 9 | RabbitMQ install + exchange setup on coordinator | **DONE** | | Task 10 | AMQP connection layer in jC (`amqp.py`, aio-pika) | **NEXT** | -| Task 11 | Node agent on jarvis — registration, heartbeat, command listeners | Pending | +| Task 11 | Node agent on worker — registration, heartbeat, command listeners | Pending | | Task 12 | AMQP wiring: inject context into chat/completions pipeline | Pending | | Task 13 | Query triage via Phi-4-mini (`triage.py`) | Pending | | Task 14 | Dynamic model swap — publish cmd, handle ready/failed | Pending | diff --git a/eviction.py b/eviction.py index 7056db9..b9ed754 100644 --- a/eviction.py +++ b/eviction.py @@ -1,5 +1,5 @@ """ -JarvisChat — Score-based RAG vector eviction with hysteresis. +cAIc — Score-based RAG vector eviction with hysteresis. """ import asyncio import logging @@ -14,7 +14,7 @@ from config import ( RAG_ACCESS_WEIGHT, RAG_AGE_WEIGHT, ) -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") eviction_lock = asyncio.Lock() EVICTION_LOG: list[dict] = [] diff --git a/gpu.py b/gpu.py index dbda207..7268e6d 100644 --- a/gpu.py +++ b/gpu.py @@ -1,11 +1,11 @@ """ -JarvisChat - AMD GPU stats via rocm-smi. +cAIc - AMD GPU stats via rocm-smi. """ import json import logging import subprocess -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") def get_gpu_stats() -> dict: diff --git a/hardware.py b/hardware.py index 69d8346..67b3b4e 100644 --- a/hardware.py +++ b/hardware.py @@ -1,5 +1,5 @@ """ -JarvisChat — Startup hardware self-assessment. +cAIc — Startup hardware self-assessment. """ import asyncio import json @@ -12,7 +12,7 @@ import psutil from config import LLAMA_SERVER_BASE, SEARXNG_BASE -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") HARDWARE_STATE_PATH = Path("hardware_state.json") _TIMEOUT_EXPIRED = subprocess.TimeoutExpired diff --git a/memory.py b/memory.py index 8fd2d58..cf89ba3 100644 --- a/memory.py +++ b/memory.py @@ -1,5 +1,5 @@ """ -JarvisChat - FTS5 memory system. +cAIc - FTS5 memory system. CRUD, search, remember/forget command processing, topic detection. """ import logging @@ -10,7 +10,7 @@ from typing import Optional from db import get_db from config import MAX_MEMORY_FACT_CHARS -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") REMEMBER_PATTERNS = [ (r"remember that (.+)", "explicit"), diff --git a/rag.py b/rag.py index 8cbf80e..95e1d9f 100644 --- a/rag.py +++ b/rag.py @@ -1,5 +1,5 @@ """ -JarvisChat - RAG pipeline: Qdrant vector search + system prompt assembly. +cAIc - RAG pipeline: Qdrant vector search + system prompt assembly. """ import asyncio import logging @@ -11,7 +11,7 @@ from db import get_db, get_setting, list_skills_with_state, format_active_skills from memory import search_memories from config import MAX_SKILL_PROMPT_CHARS, QDRANT_URL, RAG_COLLECTION -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") EMBED_URL = "http://192.168.50.210:11434" EMBED_MODEL = "mxbai-embed-large" diff --git a/routers/chat.py b/routers/chat.py index 662622f..8de2d23 100644 --- a/routers/chat.py +++ b/routers/chat.py @@ -18,7 +18,7 @@ from search import (calculate_perplexity, is_uncertain, is_refusal, from security import read_json_body, log_incident, BODY_LIMIT_CHAT_BYTES from config import MAX_CHAT_MESSAGE_CHARS -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") router = APIRouter() diff --git a/routers/completions.py b/routers/completions.py index cc77e02..41c700a 100644 --- a/routers/completions.py +++ b/routers/completions.py @@ -20,7 +20,7 @@ from db import get_db from rag import build_system_prompt from routers.chat import parse_llama_stream_chunk -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") router = APIRouter() diff --git a/routers/conversations.py b/routers/conversations.py index 2babf2a..bdc579c 100644 --- a/routers/conversations.py +++ b/routers/conversations.py @@ -7,7 +7,7 @@ from db import get_db from security import read_json_body, BODY_LIMIT_DEFAULT_BYTES from config import DEFAULT_MODEL, MAX_CONVERSATION_TITLE_CHARS -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") router = APIRouter() diff --git a/routers/ingest.py b/routers/ingest.py index f612f31..f093636 100644 --- a/routers/ingest.py +++ b/routers/ingest.py @@ -10,7 +10,7 @@ from config import COMPLETIONS_API_KEY from eviction import maybe_evict from rag import chunk_text, QDRANT_URL, EMBED_URL, EMBED_MODEL, RAG_COLLECTION -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") router = APIRouter() diff --git a/routers/models.py b/routers/models.py index 4e4b6c8..ae1020e 100644 --- a/routers/models.py +++ b/routers/models.py @@ -12,7 +12,7 @@ from config import LLAMA_SERVER_BASE from gpu import get_gpu_stats from security import read_json_body, BODY_LIMIT_DEFAULT_BYTES -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") router = APIRouter() diff --git a/routers/rag_admin.py b/routers/rag_admin.py index 53736f7..6235ba3 100644 --- a/routers/rag_admin.py +++ b/routers/rag_admin.py @@ -8,7 +8,7 @@ from fastapi.responses import JSONResponse from eviction import get_rag_operational_stats, EVICTION_LOG from rag import QDRANT_URL, RAG_COLLECTION -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") router = APIRouter() diff --git a/routers/search_route.py b/routers/search_route.py index 72560e7..c17a459 100644 --- a/routers/search_route.py +++ b/routers/search_route.py @@ -14,7 +14,7 @@ from search import query_searxng, format_search_results from routers.chat import parse_llama_stream_chunk from security import read_json_body, log_incident, BODY_LIMIT_CHAT_BYTES -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") router = APIRouter() diff --git a/routers/upload.py b/routers/upload.py index 40bc552..bee6d39 100644 --- a/routers/upload.py +++ b/routers/upload.py @@ -13,7 +13,7 @@ from db import get_db, insert_upload_context, list_upload_context_by_conversatio from eviction import maybe_evict from rag import chunk_text, QDRANT_URL, EMBED_URL, EMBED_MODEL, RAG_COLLECTION -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") router = APIRouter() diff --git a/search.py b/search.py index 233e616..f911580 100644 --- a/search.py +++ b/search.py @@ -1,5 +1,5 @@ """ -JarvisChat - SearXNG integration, perplexity scoring, refusal/hedge detection. +cAIc - SearXNG integration, perplexity scoring, refusal/hedge detection. """ import logging import math @@ -10,7 +10,7 @@ import httpx from config import SEARXNG_BASE, PERPLEXITY_THRESHOLD, REFUSAL_PATTERNS, HEDGE_PATTERNS -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") def sanitize_outbound_url(url: str) -> str: diff --git a/security.py b/security.py index 2c120eb..9b0e73a 100644 --- a/security.py +++ b/security.py @@ -1,5 +1,5 @@ """ -JarvisChat - Security utilities. +cAIc - Security utilities. PIN hashing, audit logging, incident tracking, CSRF/origin checks, rate limiting, request helpers. """ @@ -30,7 +30,7 @@ from config import ( import ipaddress -log = logging.getLogger("jarvischat") +log = logging.getLogger("caic") SESSIONS: dict = {} PIN_ATTEMPTS: dict = {} diff --git a/templates/index.html b/templates/index.html index 0b42a74..e68d3ef 100644 --- a/templates/index.html +++ b/templates/index.html @@ -3,7 +3,7 @@ -JarvisChat +cAIc @@ -263,7 +263,7 @@ body { font-family: var(--font-body); background: var(--bg-primary); color: var(
-
JarvisChat Unlock
+
cAIc Unlock
Enter 4-digit admin PIN to unlock advanced actions
Security warning: PIN 1234 is weak. Use a non-trivial 4-digit PIN.
@@ -276,8 +276,8 @@ body { font-family: var(--font-body); background: var(--bg-primary); color: var(