Admin guide
How to run, update, and operate this SHAB search demonstrator. For searching the index, use the user guide.
Sources Stack Start the GUI Daily update Full rebuild Company metadata Operations
SHAB, Zefix, and LINDAS
SHAB (Schweizerisches Handelsamtsblatt) is the Swiss Official Gazette of Commerce. Notices are ingested from ItemStorage (SHAB gatherer).
Zefix (Zentraler Firmenindex) is the Confederation’s commercial-register search. Optional SHAB_DAILY_ZEFIX=1 calls Zefix REST for companies LINDAS does not resolve. GUI register links point at Zefix when seat and EHRAID are known.
LINDAS (Linked Data Service of the Swiss Confederation) is the open SPARQL catalogue we query for CHE↔CH-ID aliases, purpose, legal form, seat, EHRAID, street address, and other registered names (de/fr/it/en).
Stack
The GUI and REST API read Postgres table shab_entities. Source items come from ItemStorage (SHAB gatherer). Parsed JSONL lives under data/shab/. Company UID/CH-ID aliases and LINDAS enrichment live under data/shab/aliases/.
Settings: config/app.ini (host, port, table). Credentials: config/secrets.ini, merged over sibling OpenSanctions secrets when present. Search uses [database_opensanctions]; fetch uses [database_itemstorage] or [database_kyc].
Start the GUI
cd /data/projects/TOYS/toys/trunk/shab-search source venv/bin/activate python run.py
Default URL is the host and port in app.ini (this instance uses port 5088). Keep debug = false on this host. After Python changes, restart the process. After CSS or JS changes, hard-refresh the browser. Health: /api/health.
Daily update
Incremental ingest does not re-parse the full dump. Cron example:
30 3 * * * /data/projects/TOYS/toys/trunk/shab-search/tools/shab_daily_update.sh
The script fetches ItemStorage changes since the watermark, then runs shab_apply_update.py. That step:
- ingests the delta into person, company, and notice JSONL;
- re-queries LINDAS for companies in the delta and companies mentioned on new notices (including keys already aliased);
- masters affected CHE/CH-ID groups and writes LINDAS fields (seat, purpose, address, EHRAID, registered names) onto those company bodies;
- patches
data/shab/entities/mastered/*-shab.jsonland upserts the same rows into Postgres.
Apply is skipped when fetch reports no new items. One job at a time (lock file). Do not start a second apply while one is running. Logs: logs/daily_update.log and logs/daily_update-YYYY-MM-DD.log.
Environment: SHAB_DAILY_SKIP_DB=1 writes JSONL only. SHAB_DAILY_ZEFIX=1 uses Zefix REST for LINDAS misses.
Full rebuild
Use this when JSONL and Postgres have drifted, or after a first load:
python tools/shab_fetch.py adopt # or full python tools/shab_parse.py python tools/shab_master_companies.py run python tools/shab_load.py --truncate --from-mastered
Once per database: sql/add_shab_entities_key_unique.sql (required for upsert), sql/add_company_lindas_fields.sql, and sql/add_company_names.sql. Search indexes: python apply_indexes.py.
Company metadata (LINDAS)
LINDAS supplies CHE↔CH-ID aliases, purpose, legal form, seat, EHRAID, street address, and other registered names (schema:legalName plus language-tagged schema:name). Build or refresh the local snapshot with python tools/shab_company_aliases.py build. --re-lindas rewrites company_lindas.jsonl (hours; required for names on the full corpus). update-keys --refresh CHE-… refreshes one firm.
Daily apply already refreshes LINDAS for companies in that day’s notices and writes both mastered JSONL and Postgres for those keys.
To copy LINDAS streets onto the whole table (or mastered JSONL) without waiting for each firm to appear in an update:
python tools/shab_backfill_lindas_addresses.py python tools/shab_backfill_lindas_addresses.py --jsonl --skip-db
Registered names (after sql/add_company_names.sql and a LINDAS refresh that stores names):
python tools/shab_backfill_lindas_names.py --keys CHE-105.940.833 --fetch --jsonl python tools/shab_backfill_lindas_names.py --from-jsonl
Re-running the Postgres address backfill after it has succeeded reports updated=0. Firms with no LINDAS record keep the SHAB place name only. Full-corpus names need --re-lindas first; until then only refreshed keys (daily apply or --fetch) get translations.
Operations notes
- If connect fails on port 6432 (pgbouncer), try direct Postgres 5432 with the same credentials, or restore pgbouncer.
- Company pages skip a cold parse of the LINDAS file; a background thread warms that index after start.
- Detailed pipeline notes live in the repository:
docs/SHAB_PIPELINE.md.
API reference in the GUI: REST API.