server/README.md
Fact archive
Aegis shared archive server
Every Aegis phone downloads checked facts from this archive. Phones whose owners agreed also point it at subjects they researched. The server never publishes what a phone sends: a phone sends only Wikidata item and property numbers ("Q90 P1082 is stated in its article"), and verify.php fetches the item and the article itself, runs the same checks the phone runs (mobile/engine/curiosity.py, ported in lib/check.php), and publishes only values and lead text from its own fetch.
Requirements
- PHP 8.1 or newer with PDO MySQL. The curl extension is used when present; otherwise PHP's own HTTPS streams (
allow_url_fopen = On). - MySQL 5.7+ or MariaDB 10.3+.
- Outbound HTTPS to
www.wikidata.organden.wikipedia.org. - A cron job (or a web-cron service) that runs
verify.phpevery 5 minutes. - No Composer packages.
Install
- Upload the
server/folder, for example topublic_html/aegis/. Onlyapi.php,admin.phpandverify.phpare entry points;.htaccessblockslib/,tests/,data/,config.phpandschema.sqlon Apache. On nginx, deny those paths yourself. - Create a database and user in the hosting panel, then import the schema:
mysql -u USER -p DBNAME < schema.sql(or phpMyAdmin, Import). - Copy
config.sample.phptoconfig.phpand set the database login,salt,admin_password_hash,cron_keyanduser_agent(with your site and an email address; Wikimedia asks for one). - Cron, every 5 minutes:
/5 * php /home/USER/public_html/aegis/verify.php >/dev/null 2>&1Without cron, point a web-cron service athttps://YOUR.SITE/aegis/verify.php?key=CRON_KEY. - Check
https://YOUR.SITE/aegis/api.php?r=v1/status. It should return JSON counts. - Put the base URL (
https://YOUR.SITE/aegis) intoARCHIVE_URLinmobile/engine/archive.pybefore building the phone app (or set it on a phone in Settings, Shared archive URL).
API
All responses are JSON. Routes work as api.php/v1/... or api.php?r=v1/... (no rewrite rules needed). Reads need no account; writes need Authorization: Bearer DEVICE.TOKEN.
| Route | What it does |
|---|---|
GET v1/status | Counts: subjects, facts, devices, pending refs, latest seq |
GET v1/facts?since=SEQ&limit=500 | Published changes after SEQ (withdrawals too), with their subjects |
GET v1/bucket/abcd | Every published fact for subjects with a name whose sha256 starts with abcd |
POST v1/register | A new random device id and token (5 per IP per day) |
POST v1/lease | Up to 5 subjects nobody researched in 30 days, leased for 6 hours |
POST v1/contribute | {"items":[{"qid":"Q90","title":"Paris","pids":["P1082"],"about":true}]}, at most 50 items, 300 refs per device and 3000 per IP a day |
Names are normalized the same way on both sides: underscores to spaces, whitespace collapsed, trimmed, lower case (name_key in lib/check.php and mobile/engine/archive.py).
Verification (verify.php)
Each run takes up to 100 subjects with pending refs, most-agreed first, and for each:
- fetches the Wikidata item and its English Wikipedia lead;
- rejects it when it is not a real subject (no English article, a disambiguation page, a list, a year or a date);
- publishes each property the lead also states (years equal, numbers within 2%, 10% for population, item values named) and the first sentences of the lead;
- marks the refs accepted, unconfirmed (the lead does not say it) or rejected (Wikidata has no such value, or the lead says something else). Rejected refs count against the devices that sent them; a device with more than 60% rejected after 50 refs is suspended;
- queues the subjects the item's facts name (capital, founder, country, ...) as research for phones to lease.
It also re-checks up to 20 subjects published more than 90 days ago and withdraws facts Wikidata or the article no longer support. Runs never overlap (a lock file).
Admin
admin.php (one password, from admin_password_hash): counts, newest facts, and every device the API has registered (id, status, sent, accepted, rejected, first seen, last seen). The page does not show tokens or IP hashes. Buttons withdraw a fact or suspend a device.
Tests
php tests/test_check.php # PHP checks against vectors from curiosity.py
php tests/test_api.php # API and verification on the in-memory store
php tests/test_api.php --mysql 'mysql:host=localhost;dbname=aegis_test' USER PASS
# the same tests on a real, empty MySQL database
python3 tests/make_vectors.py # regenerate vectors.json after curiosity.py changes