Generate and edit Draw.io, Mermaid, and Excalidraw diagrams from natural language using a structured JSON spec.
Memory
Iran Chemical Database
Try itIran Chemical Database โ live, dated, auditable, BEST-EFFORT index of chemical offerings in configured public Iranian supplier catalogues (websites + public Telegram channels). HTTrack/WooCommerce-REST/Telegram mirroring โ local-only parsing โ RDKit/PubChem/CAS-validated PostgreSQL with FastAPI + Streamlit. Fail-closed Iranian-suppliers-only country gate; coverage measured and published, never claimed complete. Installation = software + queued crawl, not a populated dataset. Ships a 1399-molecule CID-unique confirmed-organic seed baseline (v2.22, 2026-08-27: v2.19 primary + live Telegram/WooCommerce/sitemap crawl + 5-model fleet normalization, every new identity PubChem-confirmed). For academic procurement research.
What it does
๐งช Iran Chemical Database โ HTTrack-Powered Live Crawling System
The skill document
๐งช Iran Chemical Database โ HTTrack-Powered Live Crawling System
๐ด IMPORTANT: Installation provides SOFTWARE, not a populated dataset. A successful installation means services and jobs are initialized; the initial crawl is QUEUED and may take hours or days. Never describe the database as "complete" right after installation โ check
/api/v1/coverage.
This skill is a dated, auditable, best-effort index of confirmed and
unresolved chemical offerings discovered in configured public Iranian supplier
catalogues. What the system has and has NOT covered is measured and
published via /api/v1/coverage; no web crawler can guarantee "all organic
molecules available in Iran", and this skill never claims to.
๐ด MANDATORY AGENT INSTRUCTIONS
Before answering ANY request for molecules from this database:
- Check
/api/v1/coveragefirst. - State whether the configured supplier crawl is complete, partial, or still running.
- Never infer national availability from this database โ it indexes the configured suppliers' public catalogues only.
- Never make a "complete" CSV from
/api/v1/molecules: it is paginated (default 20 rows,limitโค 100, returnstotal_pages/has_more). For a full export use/api/v1/export(not page-limited). - Call
organic_status=true"confirmed organic", never "all organic"; unresolved records areunknownand are exported separately, never silently discarded. - Include the export metadata/manifest (
format=manifestor the CSV's# export_metadata:line) and report its row count when presenting files.
Example full exports:
# All identified catalogue molecules, preserving organic uncertainty
curl -L 'http://localhost/api/v1/export?format=csv&shape=molecules&organic_status=all' \
-o iran-catalogue-molecules-all-statuses.csv
# Only confirmed-organic molecules
curl -L 'http://localhost/api/v1/export?format=csv&shape=molecules&organic_status=true' \
-o iran-confirmed-organic-molecules.csv
# Unresolved organic status โ queue for remediation/review
curl -L 'http://localhost/api/v1/export?format=csv&shape=molecules&organic_status=unknown' \
-o iran-organic-status-unknown.csv
# Machine-readable export manifest (SHA-256 + row count + coverage snapshot)
curl -L 'http://localhost/api/v1/export?format=manifest&shape=molecules&organic_status=all'
require_complete_coverage=true makes the export return HTTP 409 until every
configured supplier has a terminal crawl state.
What it is
A production-ready Linux application that discovers, mirrors, and indexes Iranian chemical supplier websites, extracting catalogue entries into a live relational database. Coverage, crawl states (queued โ running โ success / partial / failed), rejection reasons and organic-classification uncertainty are all measured and published.
Architecture (6 modules)
- Supplier Discovery Engine โ seed list (35 curated suppliers, the first crawl cohort) + autonomous discovery (search engines EN/FA, link analysis of mirrored sites, academic citations, business registries, manual curation). Directory discovery is a separate, opt-in, strictly time-bounded task so it can never delay seed crawling. Every candidate is verified before crawling.
- HTTrack Mirror Engine โ Python wrapper around the
httrackCLI; initial mirror,--updateincremental sync,hts-changes.jsonchange detection, per-supplier crawl profiles (static/paginated/PDF-Excel/JS/login/blocked), Playwright fallback and JS/API catalogue capture with network recording. 2b. WooCommerce REST + sitemap engine (v2.5) โ the majority of catalog-carrying Iranian supplier sites are WordPress/WooCommerce storefronts.src/crawler/woo_rest_engine.pyfetches their PUBLIC, unauthenticated product API (/wp-json/wc/store/v1/products?per_page=100) plussitemap.xml/product-sitemap.xml, and persists the JSON into the local mirror store โ the existing local-file-only parser consumes it with no network access. Cheap, structured, and far faster than a full mirror. - Molecule Parser & Classifier โ parses LOCAL mirror files only (HTML,
JSON-LD, PDF, Excel, CSV, DOCX, JSON-API payloads, Woo REST JSON); configurable
inclusion policy (
research_only|lab_or_research|all_identifiable_catalogue, defaultall_identifiable_catalogue); explicit structure-first organic classification; RDKit + PubChem + CAS-checksum validation; deterministic source identity (real InChIKeys only); every rejection preserved in an audit table with stage + reason. - Database Engine โ PostgreSQL (SQLAlchemy): suppliers / molecules / supplier_offerings / httrack_mirrors / crawl_log / crawl_run_state / offering_history / rejected_catalogue_items; live sync inserts new, updates changed, discontinues removed products.
- UI & API โ FastAPI REST API (
/api/v1/*incl.coverage,jobs,rejections,reconciliation, fullexport+ JSON manifest), Streamlit dashboard (search, coverage & jobs, rejection audit, reconciliation, export readiness). - Health & observability โ
python -m src.scripts.healthverifies the stack AND data readiness (INITIALIZED vs OK), distinguishing a fresh install from a populated database.
Quick start (Docker โ the authoritative path)
cp .env.example .env # REQUIRED: set a strong DB_PASSWORD (installer refuses placeholders)
./install.sh # system packages, migrations, seeding, QUEUES the initial crawl
docker compose up -d # api :8000, dashboard :8501, nginx :80, crawler+scheduler workers
python -m src.scripts.health # stack + data readiness
Quick start (bare metal โ complete)
sudo apt install httrack
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt && playwright install chromium
cp .env.example .env # set DB_PASSWORD; SEARCH_API_KEY optional (seed-only discovery works without it)
docker compose up -d postgres redis # or run PostgreSQL+Redis any way you like (config.yaml)
alembic -c alembic/alembic.ini upgrade head
python -m src.scripts.seed_suppliers
python -m src.scripts.trigger_initial_crawl # QUEUES seed crawling (returns in seconds)
celery -A src.tasks.celery_app worker --loglevel=info & # worker (required for the queued crawl)
celery -A src.tasks.celery_app beat --loglevel=info & # scheduler (hourly sweep + weekly discovery)
uvicorn src.api.app:app --port 8000
Dashboard at http://localhost:8501, API at http://localhost:8000/api/v1/
(Docker: nginx fronts it on port 80).
Inclusion policy & reparse
# config.yaml: parsing.inclusion_mode = research_only | lab_or_research | all_identifiable_catalogue
# Re-apply a policy to every existing mirror without re-downloading:
python -m src.scripts.reparse_all_mirrors --inclusion-mode all_identifiable_catalogue
# (reports candidates/accepted/per-reason rejections/sync errors; nonzero exit
# above parsing.reparse_failure_threshold)
Excluded entries are never silently dropped โ they live in
rejected_catalogue_items with rejection_stage + rejection_reason
(queryable via /api/v1/rejections, dashboard tab "Rejections").
Requirements checklist (see docs/architecture.md)
HTTrack primary engine ยท --update live sync ยท hts-changes.json selective
parsing ยท Playwright fallback + JSON-API capture ยท parser reads LOCAL files
only ยท measured coverage (no "zero supplier gaps" claim) ยท configurable
inclusion policy + reparse ยท explicit organic classification
(structure-first, lookup errors recorded, unknown exported separately) ยท
persisted queued/running/terminal crawl states ยท paginated endpoints announce
pagination (total_pages/has_more) ยท full export + JSON manifest ยท
rejection audit table ยท reconciliation reports ยท live database ยท Persian NLP ยท
RDKit/PubChem validation ยท deterministic source identity (real InChIKeys
only) ยท httrack in Docker ยท persistent mirror volume ยท polite crawling ยท
documented ยท tested.
๐ v2.8 โ multi-tool HTTP fetch fallback (curl / wget / python)
HTTrack is the primary mirror engine, but it can be missing, its default
User-Agent blocked, or a catalog may need only a few pages. The new
src/crawler/http_fetch_engine.py provides graceful fallbacks:
- python-urllib (always available, no binary) โ curl (browser UA,
-Lredirects) โ wget (browser UA,--tries=2) โ first success wins; - optional
wget -r -k -precursive mirror as a last-resort site downloader; - tools are detected at runtime (
shutil.which); a missing tool is skipped; - fetched pages land in
/fetch-fallback//with the right extension (.html/.json/.pdf/...) and flow into the existing local-file parser; crawl_tasks.pynow degrades gracefully when HTTrack is missing or errors, and runs the HTTP fallback when a mirror comes back empty (but not geo-blocked โ those go to the free-access engine). Coverage counts the files.
Config: http_fetch: section (enabled, timeout, delay, wget_recursive +
depth). Zero new dependencies.
๐ v2.7.1 โ Wayback "Save Page Now" (invented via adversarial debate)
Two debate rounds among the reasoning team produced a new, live-verified method:
SPN2 โ https://web.archive.org/save/ forces the Internet Archive
crawler to fetch a blocked page FRESH from its own (allowed) IPs, then reads the
capture back. Verified live on rockchemie.com (428 KB, 2026-08-22). The method
is appended to every supplier's free-access list automatically and fails
gracefully when IA is busy.
๐ v2.7 โ Common Crawl + screenshot fetchers
Exhaustive round-3 sweep added two more free fetchers:
- Common Crawl โ
https://index.commoncrawl.org(index) +https://data.commoncrawl.org(WARC store, S3 โ not geo-blocked). Returns RECENT full-HTML captures via tiny HTTP Range requests. Verified live: rockchemie.com 27 captures (Jul 2026), pgsoc.ir 1, irandaru.com 3, shimico.com 407. This gives the "Wayback-only" sites (pgsoc, novichem) a second, fresher source. Saved under/free-access/commoncrawl/. - thum.io screenshot โ
https://image.thum.io/get/width/1200/renders the page server-side to a PNG (verified: 492 KB render of rockchemie.com). Image-only visual evidence; opt-in (screenshot), not in the default list.
DEFAULT_FREE_ACCESS_METHODS is now jina, wayback, commoncrawl, translate, archivetoday.
๐ v2.6 โ free-access fallback for geo-blocked Iranian sites
Field-verified 2026-08-21 on the 12 geo-blocked seed sites (rockchemie.com, abnoos.com, artinkimya.com, pakshoo.com, pgsoc.ir, tebgostar.com, novichem.ir, basparsazan.com, mahdistejarat.com, irandaru.com, shimico.com, parsisotope.com): every one of them is reachable through at least one FREE third-party fetcher whose own IPs are not on the Iranian hosts' blocklist.
- Jina Reader โ
https://r.jina.ai/returns the page as markdown text (worked on 9/12 sites). Saved as.mdunder/free-access/jina/. - Wayback Machine โ the CDX API enumerates archived snapshots;
https://web.archive.org/web/id_/serves the raw HTML (10/12 sites). Saved as.htmlunder/free-access/wayback/. - Google Translate proxy โ
translate.google.com/translate?u=fetches server-side (9/12 sites). Saved as.htmlunder/free-access/translate/. - archive.today (v2.6.1) โ
archive.ph/newest/serves an existing snapshot; reachable from residential/operator networks (blocks many datacenter IPs, fails gracefully). Saved under/free-access/archivetoday/.
src/crawler/free_access_engine.py implements all four (stdlib only, no keys);
src/parser/markdown_parser.py extracts CAS-bearing molecule candidates from
the Jina text. When a mirror looks geo-blocked (zero files + TLS/timeout
signature), crawl_tasks.py runs the free-access engine automatically and the
saved files feed the normal local-file parse pass โ so /coverage reflects
real fetched content instead of a false "no-html-mirrored".
Per-site preferences (v2.6.1): every geo-blocked seed entry carries a
field-verified free_access_methods list โ e.g. novichem.ir and pgsoc.ir are
Wayback-only (their WAFs reset even Jina/Translate), basparsazan.com uses
["jina","translate"], artinkimya.com uses all three. free_access_preference()
in seed_list.py picks the right methods per site; unknown domains fall back
to free_access.methods in config.yaml.
๐ v2.5 โ what changed (field-hardened)
- WooCommerce REST + sitemap engine (
src/crawler/woo_rest_engine.py): the dominant catalog engine among the seeded Iranian suppliers is WordPress/WooCommerce. The public Store API (/wp-json/wc/store/v1/products) needs no key and returns structured products;sitemap.xml/product-sitemap.xmlenumerate product URLs cheaply. Fetched JSON lands in the local mirror store and is parsed by the existing local-file-only pipeline. - Fingerprint-annotated seed list (
src/discovery/seed_list.py): every one of the 35 suppliers now carries its 2026-08 live-probe status โactivevsinactive(12 dead domains, 2 parked, 1 inorganic-only, 1 radiopharma), a crawl-profile hint (woo_rest/sitemap_wp/playwright_js/ โฆ), notes (geo-blocked, WAF) and concrete REST/sitemap entry points. Dead domains are seededinactiveand skipped bymirror_all_suppliersโ zero crawl budget wasted. - Geo-block detection (
_looks_geo_blocked): a mirror that yields zero files with an SSL/TLS/handshake/timeout signature โ the failure mode of ~13 live.irhosts that reject foreign datacenter IPs โ is flaggedgeo-blocked-possible (retry via Iranian proxy)instead of being retried blindly. - Celery dispatch fix (
trigger_initial_crawl.py): the previous release called.delay()on a bare@shared_taskwithout the Redis-boundcelery_appas current app, so Celery fell back to the AMQP broker and the seed dispatch failed with "Connection refused" while the worker was healthy. All dispatch now goes throughcelery_app.send_task(...).
โ Verify before relying on it (v2.9)
python3 scripts/preflight.py # environment readiness (httrack/curl/wget)
python3 scripts/self_test.py # offline self-test (compile + files + imports + pytest subset)
python3 tools/package_selftest.py # release readiness (run before publishing)
python3 -m pytest -q # full suite (DB tests self-skip without PostgreSQL)
python3 -m ruff check src/ scripts/ tools/ # bug-focused lint (E/F/W/B)
CI (.github/workflows/ci.yml) runs all of the above on every push and PR.
๐ Security posture
- Outbound is crawling-only. The
network.outbound: ["*"]declaration is by design: the whole purpose is mirroring arbitrary supplier websites. In practice the app only makes requests to (a) supplier/B2B URLs you configure, and (b) optional public APIs (PubChem for validation, search APIs if you provide a key). Nothing is uploaded about you or your machine. - No secrets in the skill.
DB_PASSWORD/SEARCH_API_KEYcome from the environment (.env, gitignored);.env.exampleships WITH the release. - Local-first. The parser reads only local mirror files; mirrors live under
/var/lib/iran_chem_db/mirrors. - Polite crawling. robots.txt honored, rate-limited, identifiable User-Agent, per-supplier overrides.
- Production hardening (see
docs/deployment_guide.md): put the API behind nginx with authentication, allowlist egress to supplier domains if you use a firewall, and treat the API/dashboard as trusted-network services by default.
Legal & ethical use
Only mirror websites you are authorized to archive. Respect robots.txt and site terms of service. The database is a research/procurement reference โ verify every supplier and molecule before relying on it.
Files
See README.md (security/privacy + verification hashes), CHANGELOG.md,
docs/ (architecture, API reference, deployment), tests/, and the full
src/ tree.
Related skills
Trade crypto, manage a multi-chain wallet, and query an AI analyst from one CLI.
Stores durable facts in a categorized, plain-markdown vault on disk, alongside your agent's built-in memory.
Fetch raw ad creative, app, ranking, and revenue data from AdMapix as structured JSON.
Adaptive web scraping in Python that bypasses anti-bot systems and scales from single requests to concurrent crawls.
Save, search, and manage personal notes and knowledge bases in Get็ฌ่ฎฐ on explicit request.
More from orionshaowswmw
Browse all skillsDetect and repair partially wiped agent workspaces with integrity checks, signed manifests, guarded restore recipes, bounded local recovery state, and explicit off-box sync. Use when files, scripts, trees, models, or build outputs disappear or lose integrity between turns.
Seven offline mechanisms against slow/stale/zombie/sycophantic agent turns: prompt compaction, request fencing, zombie detection, CAPTCHA triage, anti-sycophancy spine, delivery register, invention quarry. Use when chat feels laggy, reconnects surface old answers, long chats degrade, or the agent caves under contradiction. JSON contracts; state per-agent under ~/.arena_turn; no network, no sudo.
Quota-aware LLM router that squeezes maximum usable AI out of free-tier API keys across Gemini, Mistral, OpenRouter, Kilo and Cerebras plus any OpenAI-compatible endpoint (including local Ollama/llama.cpp/vLLM). Probes every model on every key, measures real quality and real published rate limits, then routes each request to the cheapest model that can do the job โ spending abundant capacity first and reserving scarce daily quota for when it is actually needed. Persists cooldowns to disk so a 429 discovered in one process is respected by the next. Use when an agent must make many LLM calls on free keys without hitting rate limits, when "all models failed", or when deciding which of several provider keys to use for a task.
Opt-in, model-neutral guidance for evidence-aware, dignified AI communication, with a compact response contract and offline deterministic text audit. It never injects prompts, edits host configuration, calls networks, reads secrets, or treats heuristics as truth.
Model-agnostic, agent-agnostic fidelity-first pipeline converting operator-authorized Persian/English RTL lecture PDFs into offline HTML study guides โ recall-first dual OCR (PyMuPDF + Tesseract fas+eng PSM ensemble), rendered-page evidence, multi-model correction, session-grounded enrichment (tables/flashcards/quizzes/mnemonics/summaries/scenarios), measured fidelity, QA gates, verified ZIP. v1.5.0 runs on ANY model family through 8 API dialects (OpenAI, Responses, Gemini, Anthropic, Cohere, Ollama, HuggingFace, offline mock) or with no model at all, auto-discovers providers from the host agent's environment, self-heals provider quirks and model retirements, and exposes one deterministic CLI/MCP entrypoint plus cross-model consensus so different agents reproduce the same intended result.
Anti-stuck/anti-snapshot-wipe guard for agentic sandboxes with actual selfheal_runner.sh library, byte-verified GGUF manifest, native CPU rebuild +7-10%, har...