On this page

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

Install

  1. Upload the server/ folder, for example to public_html/aegis/. Only api.php, admin.php and verify.php are entry points; .htaccess blocks lib/, tests/, data/, config.php and schema.sql on Apache. On nginx, deny those paths yourself.
  2. Create a database and user in the hosting panel, then import the schema: mysql -u USER -p DBNAME < schema.sql (or phpMyAdmin, Import).
  3. Copy config.sample.php to config.php and set the database login, salt, admin_password_hash, cron_key and user_agent (with your site and an email address; Wikimedia asks for one).
  4. Cron, every 5 minutes: /5 * php /home/USER/public_html/aegis/verify.php >/dev/null 2>&1 Without cron, point a web-cron service at https://YOUR.SITE/aegis/verify.php?key=CRON_KEY.
  5. Check https://YOUR.SITE/aegis/api.php?r=v1/status. It should return JSON counts.
  6. Put the base URL (https://YOUR.SITE/aegis) into ARCHIVE_URL in mobile/engine/archive.py before 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.

RouteWhat it does
GET v1/statusCounts: subjects, facts, devices, pending refs, latest seq
GET v1/facts?since=SEQ&limit=500Published changes after SEQ (withdrawals too), with their subjects
GET v1/bucket/abcdEvery published fact for subjects with a name whose sha256 starts with abcd
POST v1/registerA new random device id and token (5 per IP per day)
POST v1/leaseUp 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:

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