Reports: the LLM reliably used company_enrichment for prose fields but
inconsistently populated the parallel Finding-list/string-list fields from
the same evidence, even with progressively more explicit prompting. Add a
code-level backfill (products, recent developments, financial signals,
strategic initiatives, regulatory signals, risks/opportunities mirrored
from SWOT, unknowns, monitoring recommendations) that only ever fills in
what the model left empty, never overwrites what it produced.
Enrichment tab: reorder sections (Products/Recent updates before
Customers/Competitors) and add a per-section "Refresh" button that
re-fetches just one of NinjaPear's six independent per-company endpoints
when it came back empty - confirmed live that a data-coverage gap (e.g.
Amazon returning no products) is real provider behavior, not a bug.
Auth: the first account registered on a deployment with zero existing
admins is now auto-promoted to admin, closing the chicken-and-egg gap
where the only path to admin access was direct DB access. Self-heals if
the last admin ever deletes their account.
Also bumps nginx's proxy_read_timeout for api.ciagent.org to cover the
enrichment refresh's synchronous funding-endpoint call (up to 5 minutes
per NinjaPear's docs).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
nginx resolves the api/web service names to a container IP once, at
its own worker-process startup. docker compose up -d only recreates
containers whose image/config changed, so nginx keeps proxying to the
old, now-dead IP after a deploy rebuilds those containers - every
request 502s, which shows up in the browser as a misleading CORS
error since the bare 502 carries no Access-Control-Allow-Origin
header. Confirmed live: this caused a real multi-hour production
outage after the last two deploys.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Local dev gets an unauthenticated Adminer instance bound to loopback
only. In production, any account with is_admin=true can open it -
the app mints a short-lived token from a live admin session, which
Nginx's new db.ciagent.org block exchanges for a session cookie that
re-checks admin status on every request, instead of a shared static
password that wouldn't scale to multiple admins or revoke live.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
scripts/auto-deploy.sh + a systemd timer (2min interval) that fetches
origin/master and, if ahead, pulls/rebuilds/migrates/restarts - same
sequence as the manual update steps in DEPLOYMENT.md, just scheduled.
Polling instead of a Gitea webhook deliberately: no extra exposed
service, no Docker socket mounted into a container, no shared secret
to manage - it's the same trust boundary as a manual SSH deploy.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Sets up everything needed to deploy behind Cloudflare with a self-hosted
git server: multi-stage prod Dockerfiles (non-root), docker-compose.prod.yml
(Postgres/Redis with no host ports, Nginx reverse proxy, Gitea with
public-read/admin-write access control), scripts/bootstrap-env.sh to
auto-generate required secrets on first clone, and DEPLOYMENT.md covering
the full runbook. Provider API keys (Anthropic/Brave/NinjaPear/USPTO/
Turnstile) are deliberately kept out of .env in favor of the existing
DB-backed Settings UI, so the public repo stays safe to expose.
Also fixes two bugs only surfaced by live-testing the prod stack: Celery
beat couldn't write its schedule file as a non-root user, and Gitea's
embedded SSH server conflicted with the base image's own sshd on port 22.
Co-Authored-By: Claude Sonnet 5 <[email protected]>