Install it, run it, and check that it worked
One Python process on one machine. No container, no database server, no account, and no key required to see real data.
Requirements
Install
Windows, the short way
Unzip the folder and run START_SINGULARITY_WINDOWS.bat. It creates the virtual environment, installs the dependencies, and starts the server. Then open http://127.0.0.1:8023/ in Chrome or Edge. Keep the terminal window open — closing it stops the server.
Any platform, by hand
python -m venv .venv
.venv/Scripts/python.exe -m pip install --upgrade pip
.venv/Scripts/python.exe -m pip install -r requirements.txt
.venv/Scripts/python.exe work/run_phase3_server.py 8023On macOS and Linux the interpreter is .venv/bin/python. The launcher takes the port as its only argument; if 8023 is taken, pass another one.
Dependencies, from requirements.txt: fastapi, uvicorn[standard], httpx, pydantic, numpy, pandas, statsmodels, QuantLib-Python, networkx, yfinance, feedparser, curl_cffi, aiosqlite, beautifulsoup4, pytest, pytest-asyncio, quickjs. All are pure Python or small native wheels — no compiler, no headless browser, no ML framework.
Did it work?
This server, right now
Fetched from /api/health when this page loaded. This is your server, answering right now — not a recorded example.
The rest of the check
Four things, in order. First, the health block above should say `live` — if this page renders but that block reports a failure, the fault is in the route, not the install. Second, open / and confirm the landing page paints with real figures rather than dashes. Third, open /app?cmd=SOVW and give it fifteen seconds: screens fetch from upstream sources and a reading taken at two seconds is a loading skeleton, not a broken screen. Fourth, open /api to see the server's own route table — if it lists routes, the schema and the frontend agree.
A screen that reports an unavailable source is a working install, not a broken one. The upstreams here are rate-limited publishers; the product's design is to name the source and the reason rather than fill the gap.
Environment variables
Every one of these is optional. The server starts and every screen renders with none of them set — the affected source reports itself unavailable and names what it needs. Set a variable in the server's environment and restart; there is no settings file to edit and nothing is stored in the browser.
Values are never sent to the browser. The status column below comes from a route that answers a boolean per name and nothing derived from the value, and the name list is fixed in the server so the route cannot be used to read arbitrary environment variables.
| Variable | On this machine | What it unlocks | Without it | Read by |
|---|---|---|---|---|
| FMP_API_KEY | not set | Financial Modeling Prep consensus estimates on the economic calendar. | The calendar falls back to model-implied estimates and labels them as such. | app/services/phase6.py |
| SINGULARITY_FRED_API_KEY | not set | The official FRED JSON API instead of the public CSV endpoints. | Macro series still load over CSV; some series are unavailable. | app/services/macro_rates.py |
| FINNHUB_API_KEY | not set | Finnhub as an additional company-news source. | The news hub runs on its remaining sources. | app/services/news.py |
| REDDIT_CLIENT_ID | not set | Reddit OAuth. Reddit's anonymous JSON endpoints now answer 403. | Reddit-derived chatter screens report the missing credential. | app/services/news.py |
| X_BEARER_TOKEN | not set | X/Twitter posts in the social feed. | The X source reports 'disabled — requires a paid Basic tier or above'. | app/services/news.py |
| GOVINFO_API_KEY | not set | GovInfo as a source in business-intelligence research. | That source is skipped and the reason is shown. | app/services/phase8.py |
| REGULATIONS_GOV_API_KEY | not set | Regulations.gov dockets in business-intelligence research. | That source is skipped and the reason is shown. | app/services/phase8.py |
| PATENTSVIEW_API_KEY | not set | The PatentsView search API behind the patent complexity scorer. | The screen states which credential it requires. | backend/patent_scorer.py |
| AIS_API_KEY | not set | Worldwide AIS vessel positions on the global map. | The vessel layer is not drawn and reports 'missing credential'. | app/services/geo.py |
| SINGULARITY_SMTP_HOST | not set | Email delivery for alerts. Pairs with SINGULARITY_SMTP_FROM. | Alerts still fire in-page, as desktop notifications and as sound. | app/services/notifications.py |
| SINGULARITY_SMTP_FROM | not set | The sender address on alert email. | Email delivery stays off; the alerts panel says so. | app/services/notifications.py |
0 of 11 configured on this machine, read from /api/pages/environment. That route returns a boolean per name and never a value, a length or a prefix.
Identity — set these before anyone else can reach it
These four are not credentials and are not in the table above, because they are the opposite of a credential: every one of them exists to be PUBLISHED. A security contact that is never disclosed is not a security contact, and a User-Agent that is not sent is not an identity.
Two of them ship with a working default that belongs to this project's author rather than to you. On a personal install that is harmless. On anything anyone else can reach it means an upstream, or a security researcher, would contact the wrong person.
| Variable | What it is for | If you leave it unset | Read by |
|---|---|---|---|
| SINGULARITY_CONTACT | The security contact for this install. Appears on /contact and in /.well-known/security.txt, which is the file a researcher looks for before reporting a hole. | security.txt serves a commented file with NO Contact field rather than a syntactically valid one pointing nowhere. RFC 9116 requires Contact, and a researcher who mails a dead address believes they have reported the issue. | app/main.py — security_txt, pages_environment |
| SEC_CONTACT_EMAIL | The contact inside the User-Agent this app declares to every upstream. SEC's fair-access policy requires a real one; requests without it are refused, and repeated, get an address blocked. | Falls back to the address the project ships with, which belongs to its author and not to you. SET THIS BEFORE DEPLOYING: an upstream that needs to reach the operator of your install would reach somebody else. | app/services/http.py |
| SINGULARITY_HTTP_USER_AGENT | Overrides the whole User-Agent string for non-SEC hosts. | Built as `ProjectSingularity/3.0 <SEC_CONTACT_EMAIL>`. Honest identification is not a concession here — fred.stlouisfed.org HANGS on a browser user-agent and serves 268 kB immediately to the declared one, and FRED is where the risk-free rate feeding every WACC comes from. | app/services/http.py |
| SINGULARITY_SEC_USER_AGENT | Overrides the User-Agent sent to sec.gov, data.sec.gov and efts.sec.gov. | Built from SEC_CONTACT_EMAIL as a plain `name/version email` triple. Keep that shape: efts.sec.gov rejects any UA containing a URL or a parenthetical with 403 'Undeclared Automated Tool'. | app/services/http.py |
The VALUE of each of these is disclosed by the running app — in the User-Agent it sends, or on /contact and /.well-known/security.txt. That is what they are for. Nothing else in the environment is transmitted.
Exposing it to a network
It is designed for 127.0.0.1. Password authentication over a loopback interface is appropriate; the same design on an open internet address is not something this project has hardened for or reviewed. If you bind it to a public interface, that is a decision you are making, and you own the TLS termination, the reverse proxy and the consequences.
Read the terms and privacy pages before you do it — both carry a banner explaining that they have had no legal review, which matters a great deal more once someone other than you can reach the server.