Files
sakshamandClaude Sonnet 5 18305b545c Backfill sparse report sections, add enrichment section refresh, and bootstrap first-admin
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]>
2026-08-06 17:41:02 -04:00

134 lines
4.8 KiB
Nginx Configuration File

# Reverse proxy for the three ciagent.org subdomains, sitting between
# Cloudflare (which terminates public-facing TLS and hides this origin's
# real IP) and the app's own containers. TLS here is a Cloudflare Origin CA
# certificate (Cloudflare dashboard -> SSL/TLS -> Origin Server -> Create
# Certificate; covers ciagent.org + *.ciagent.org, up to 15yr validity, only
# trusted by Cloudflare - no ACME/renewal machinery needed). Cloudflare SSL
# mode must be "Full (strict)" for this to be meaningful. See DEPLOYMENT.md.
#
# CF-Connecting-IP (the header app.core.security.get_client_ip reads once
# TRUSTED_PROXY_IP_HEADER=CF-Connecting-IP is set) needs no special handling
# here - Nginx forwards any header it doesn't explicitly touch straight
# through to the upstream unmodified.
user nginx;
worker_processes auto;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
server_tokens off;
# Bare :80 hits get redirected to :443 - Cloudflare already enforces
# HTTPS at the edge, but the origin shouldn't 400 a direct :80 probe.
server {
listen 80;
server_name ciagent.org api.ciagent.org git.ciagent.org db.ciagent.org;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name ciagent.org;
ssl_certificate /etc/nginx/certs/cloudflare-origin.pem;
ssl_certificate_key /etc/nginx/certs/cloudflare-origin.key;
location / {
proxy_pass http://web:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 443 ssl;
server_name api.ciagent.org;
ssl_certificate /etc/nginx/certs/cloudflare-origin.pem;
ssl_certificate_key /etc/nginx/certs/cloudflare-origin.key;
location / {
proxy_pass http://api:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# NinjaPear's funding endpoint is documented as taking up to 5
# minutes (see app/enrichment/ninjapear.py's _FUNDING_TIMEOUT) -
# the enrichment-section-refresh endpoint calls it synchronously,
# so nginx's 60s default would otherwise 504 before it finishes.
proxy_read_timeout 320s;
proxy_send_timeout 320s;
}
}
server {
listen 443 ssl;
server_name git.ciagent.org;
ssl_certificate /etc/nginx/certs/cloudflare-origin.pem;
ssl_certificate_key /etc/nginx/certs/cloudflare-origin.key;
# Large git pushes (Gitea's HTTPS push path).
client_max_body_size 512m;
location / {
proxy_pass http://gitea:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# Settings -> Database viewer (Adminer). Access isn't gated by a shared
# password - the app itself (app/api/v1/db_viewer.py) mints a short-lived
# token from a live admin session, which /_auth here exchanges for a
# session cookie that /_verify re-checks (including a fresh `is_admin`
# lookup) on every request via auth_request. See DEPLOYMENT.md.
server {
listen 443 ssl;
server_name db.ciagent.org;
ssl_certificate /etc/nginx/certs/cloudflare-origin.pem;
ssl_certificate_key /etc/nginx/certs/cloudflare-origin.key;
# Adminer renders a raw SQL/data editor - not meant to be framed or
# MIME-sniffed as another content type.
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
# Optional extra hardening for admins with a static IP: uncomment
# and set your own address to additionally require it alongside a
# valid session (default `satisfy all` - both must pass, not either).
# allow 203.0.113.9;
# deny all;
location = /_verify {
internal;
proxy_pass http://api:8000/api/v1/db-viewer/verify;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Cookie $http_cookie;
}
location = /_auth {
proxy_pass http://api:8000/api/v1/db-viewer/bootstrap;
proxy_set_header Host $host;
}
location / {
auth_request /_verify;
proxy_pass http://adminer:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}