# ============================================================================= # .env.example — every key, no values. Committed deliberately. # # On lin001 these live as THREE 0600 files under ~/ai/, never in Git: # ~/ai/pg-ai.env the POSTGRES_* / AGENT_DB_* block # ~/ai/langfuse.env the Langfuse SALT / NEXTAUTH_SECRET block (see below) # ~/ai/api.env everything else # Follow the ~/authelia/authelia.env precedent: chmod 0600, owned by azureuser. # ============================================================================= # --- pg-ai (~/ai/pg-ai.env) -------------------------------------------------- POSTGRES_PASSWORD= AGENT_DB_USER=agent_ro AGENT_DB_PASSWORD= # --- imh (PENDING — leave blank until Phase 4) ------------------------------- IMH_HOST=yau-sls-poc-imh IMH_PORT=1433 IMH_DB= IMH_USER=svc_agent_ro IMH_PASSWORD= USE_FIXTURES=true # flip to false when imh is live # --- local Postgres ---------------------------------------------------------- PGHOST=pg-ai PGPORT=5432 PGDATABASE=plant PGUSER=agent_ro PGPASSWORD= # --- Azure OpenAI ------------------------------------------------------------ AZURE_OPENAI_ENDPOINT= AZURE_OPENAI_API_KEY= AZURE_OPENAI_API_VERSION= CHAT_DEPLOYMENT= # flagship — final prose only CHEAP_DEPLOYMENT= # nano/mini — classifier, entities, tool selection EMBED_DEPLOYMENT= # text-embedding-3-small # --- Documents / upload UI (Phase 9) ----------------------------------------- # Two roles, and the split IS the safety boundary. db/005 carries a trigger that # stops uploads_rw setting superseded = FALSE, so the web path can make a # document less visible and never more. ingest_rw is the only one that writes # chunks or restores a document. UPLOADS_DB_USER=uploads_rw UPLOADS_DB_PASSWORD= INGEST_DB_USER=ingest_rw INGEST_DB_PASSWORD= DOCS_INBOX=/inbox MAX_UPLOAD_MB=25 # authelia | demo # authelia the actor is Remote-User from the forward-auth headers. The design. # demo the actor is TYPED ON THE FORM. Self-asserted and unverified - # exactly what the design forbids. Rows are written as `demo:` # with actor_groups = 'DEMO-UNVERIFIED' so they can never be mistaken # for authenticated ones, and every screen says so. DOC_IDENTITY_MODE=authelia # Who may approve, withdraw or restore. Comma-separated. EMPTY MEANS NOBODY and # every mutating endpoint 403s - that is the intended failure direction. This # stands in for the AD group AI_DocPublishers; swapping to the group later is a # config change, not a code change. DOC_PUBLISHERS= # --- no-LLM stub mode -------------------------------------------------------- # OFF for anything real. With it on, no model is called: the class comes from # the caller instead of the classifier and the prose is a fixed placeholder. # Every answer carries stub_mode: true and a banner. See api/stub.py for what # it proves and what it does not. NO_LLM_STUB=false # --- behaviour --------------------------------------------------------------- CLASSIFIER_CONFIDENCE_THRESHOLD=0.7 # Where the CONTROLLED copy of a procedure actually lives - a site fact, the # same for every document, so it is not in doc_chunks. Set this to the real # DMS location. The default names who to ask, which is always true and never # sends anybody to a place that does not exist. CONTROLLED_COPY_LOCATION= SITE_TIMEZONE=Australia/Sydney # storage UTC; convert once, in Cube MAX_ROWS_RETURNED=5000 QUERY_TIMEOUT_SECONDS=30 MAX_OUTPUT_TOKENS=1200 # --- Cube -------------------------------------------------------------------- CUBEJS_API_SECRET= CUBEJS_API_URL=http://cube:4000/cubejs-api/v1 # The upstream source Cube reads. ai-compose.yml says these come from this # file, and this file did not list them - so the first person to deploy Cube # had to work out from the compose comments and db/003_roles.sql what the # service actually needed. While USE_FIXTURES=true the fixtures live in pg-ai, # so this points at pg-ai with the cube_rw credentials from pg-ai.env. # At Phase 4 the whole block becomes CUBEJS_DB_TYPE=mssql against imh. CUBEJS_DB_TYPE=postgres CUBEJS_DB_HOST=pg-ai CUBEJS_DB_PORT=5432 CUBEJS_DB_NAME=plant CUBEJS_DB_USER=cube_rw # CUBE_DB_USER in pg-ai.env CUBEJS_DB_PASS= # CUBE_DB_PASSWORD in pg-ai.env # NOT CUBEJS_EXT_DB_*. Cube v1 will not use Postgres as an external # pre-aggregation store; cubestore does that job and needs no keys here. # See the note above the cube service in compose/ai-compose.yml. # --- Langfuse ---------------------------------------------------------------- # PROJECT keys, created in the Langfuse UI - not the server's own secrets # (SALT, NEXTAUTH_SECRET), which live in langfuse.env and are a different # thing. The two must be a MATCHED PAIR from the same key: a public key from # one pair with a secret from another authenticates as neither. # # Langfuse shows the secret ONCE, at creation, and stores only a hash - the # database keeps a masked form (sk-lf-...abcd) and nothing can recover it. If # it is lost, generate a new pair; there is no way to read the old one back. # # Getting this wrong is silent in both directions. Keys absent -> _langfuse() # returns None and every question is simply untraced. Keys present but wrong -> # a client is built, Langfuse rejects it, and main.py swallows the exception on # purpose, because observability must never break the answer path. Neither case # logs anything. Confirm traces are ARRIVING; do not infer it from config. LANGFUSE_HOST=http://langfuse:3000 LANGFUSE_PUBLIC_KEY= LANGFUSE_SECRET_KEY= LANGFUSE_SALT= LANGFUSE_NEXTAUTH_SECRET= LANGFUSE_DB_PASSWORD= # --- ingest ------------------------------------------------------------------ AI_DOCS_ROOT=/datadisk/ai-docs CHUNK_TOKEN_TARGET=800 # --- document upload (Phase 9) ----------------------------------------------- # Two more roles, because the component reachable from the internet must not be # the component that can write doc_chunks. See db/004_doc_uploads.sql. UPLOADS_DB_USER=uploads_rw # ai-api: the queue only, never doc_chunks UPLOADS_DB_PASSWORD= INGEST_DB_USER=ingest_rw # ai-docs-worker AND the ai-ingest CLI: # doc_chunks + the queue. PGUSER is agent_ro # and cannot INSERT - see BUILD-AI-CONTAINERS §16.1. INGEST_DB_PASSWORD= AI_DOCS_INBOX=/datadisk/ai-docs-inbox # writable staging; NOT the ingest root AI_DOCS_WITHDRAWN=/datadisk/ai-docs-withdrawn # withdrawn files are moved, not deleted MAX_UPLOAD_MB=50 UPLOAD_DISK_LIMIT_PCT=90 # refuse uploads above this on /datadisk ALLOWED_UPLOAD_EXTENSIONS=.pdf,.docx,.md,.txt # DIRECT membership only — Authelia does not resolve nested groups. DOC_PUBLISHER_GROUP=AI_DocPublishers # Who may curate the retrieval pool and run trimmed-pool demos. Defaults to the # publisher group; point it at a narrower AD group if that should be separate. DOC_ADMIN_GROUP=AI_DocPublishers ALLOW_SELF_APPROVAL=false # uploader approving their own document # Withdrawal (superseded = TRUE) is always available to the publisher group and # is reversible. PURGE deletes chunks and is not. Leave it off unless there is a # document that must not be in the database at all. ALLOW_PURGE=false # Retrieval pool. pool_enabled is orthogonal to superseded - see db/006_doc_pool.sql. # Below this share of documents enabled, retrieval drops to an exact scan: the # HNSW index is built over ALL embeddings and filters afterwards, so a heavily # trimmed pool can return almost nothing. This bites in exactly the demo that # trims the pool. Rehearse it. POOL_EXACT_SCAN_BELOW_PCT=50 WORKER_POLL_SECONDS=10 WORKER_LEASE_MINUTES=30 # an `ingesting` row older than this is a dead worker