This is the decision log of SupplyShore. Each ADR records a context, the decision, and its rationale so future team members understand why the system is shaped as it is. Add a new ADR whenever you adopt a tool, change architecture, or reverse a prior decision.
ADR format: Status (Proposed / Accepted / Superseded / Deprecated) · Date · Decision · Context · Consequences · Alternatives considered.
Status: Accepted · Date: 2026-08-07
Context: We run a small self-hosted stack on a 4 GB Linode; we need one reliable store for operational + supplier data, and want to avoid a microservices tax.
Decision: PostgreSQL is the primary database (system of record). Use Postgres JSONB for heterogeneous supplier dossier data and pgvector for embeddings. Do not add MongoDB or a separate vector DB yet.
Consequences: Single DB simplifies backup/ops; pgvector avoids extra infra; scale to Qdrant only when vector/payload-filtering needs grow.
Alternatives: MongoDB, Elasticsearch-as-DB, microservices — rejected (complexity/ops/memory).
Status: Accepted · Date: 2026-08-07
Context: EspoCRM is live; it has the best no-code custom-entity model and a mature REST API for n8n. Alternatives (Twenty, ERPNext, Odoo, SuiteCRM) were evaluated (03-product/crm).
Decision: Remain on EspoCRM for the short/medium term. Add Advanced Pack (Reports/BPM) + Sales Pack (Products/Quotes/Purchases) when procured. Re-evaluate Twenty at the platform phase.
Consequences: No migration risk; some procurement/reporting needs paid extensions; ERP-in-a-box is deferred.
Alternatives: Twenty (modern/AI-native, watch), ERPNext (plan B if order/inventory/accounting outgrows Sales Pack), Odoo (overkill), SuiteCRM (weaker).
Status: Accepted · Date: 2026-08-07
Context: n8n is installed and free under the Sustainable Use License for internal use.
Decision: n8n Community Edition is the automation glue (email ingest, CRM sync, notifications, discovery triggers, RFQ parsing). Use Postgres as n8n's execution DB. Add Temporal only when durable long-running business workflows (order/fulfillment) appear.
Consequences: Fast visual automation; note 2025 licensing moved SSO/Git/multi-env to a paid self-hosted tier (~€667/mo) — re-evaluate before needing those.
Alternatives: Temporal (durable, add later), Airflow/Prefect (data-pipeline focus), Windmill (code-first alternative), Huginn (dated).
Status: Accepted · Date: 2026-08-07
Context: Our AI, scraping, and data layers are Python-centric; the team wants auto-OpenAPI and async.
Decision: FastAPI is the primary backend API. Next.js (TypeScript) for frontend/portal. Reconsider Django if heavy CRUD/admin is needed.
Consequences: One language for backend+AI+scraping; great for webhooks/agents.
Alternatives: Django+DRF (admin-heavy), NestJS (if TS-first end-to-end).
Status: Accepted · Date: 2026-08-07
Context: We need reliable, stateful, multi-step supplier-research agents with deterministic control.
Decision: Use LangGraph for agent orchestration, LangChain for tool/LLM/RAG integrations, and MCP as the standard way to give agents tools (web search, browser, Postgres, EspoCRM, search). Adapt gpt-researcher patterns for deep research. Log to Langfuse.
Consequences: Deterministic, observable, portable agents; MCP keeps tooling swappable across frameworks.
Alternatives: CrewAI (prototype), OpenAI Agents SDK (if OpenAI-first), AutoGen (heavier), Eliza (overkill).
Status: Accepted · Date: 2026-08-07
Context: RAG needs vector search; we have limited memory on one box.
Decision: Start embeddings in pgvector (no new infra). Migrate to Qdrant (Rust, Apache-2.0) when scale/payload-filtering/performance requires.
Consequences: Lowest ops overhead early; clean migration path.
Alternatives: Chroma (dev), Milvus (heavy), Elasticsearch (memory-heavy).
Status: Accepted · Date: 2026-08-07
Context: We need fast, typo-tolerant supplier/product search.
Decision: Use Meilisearch (or Typesense) as the standalone search service; keep Postgres full-text + pgvector for the structured/vector path. Add Elasticsearch only at large scale.
Consequences: Light, fast, easy to self-host on modest resources.
Alternatives: Elasticsearch (powerful but heavy on 4 GB).
Status: Accepted · Date: 2026-08-07
Context: Buyers need trustworthy supplier dossiers; trust is our moat.
Decision: All suppliers flow through Discovery → Research → Verification → Relationship. Score (0–100) + fit-score vs. RFQ. Desk-verify first; use 3rd-party audits (QIMA/V-Trust/HQTS; SGS/BV/Intertek/TÜV for large orders) for verification. Prefer buyer-paid verification to preserve objectivity.
Consequences: Compounding data asset; but verification has cost/time — prioritize by score.
Alternatives: Cheap raw lists (no trust), supplier-paid verification (objectivity risk — deferred).
Status: Accepted · Date: 2026-08-07
Context: Data loss/breach would destroy the business; we self-host.
Decision: Enforce the 04-security/security checklist: SSH keys only, firewall (22/80/443), all apps behind Traefik TLS, secrets manager, nightly encrypted offsite backups (3-2-1), tested restores, GDPR/UK GDPR compliance for EU/UK buyer data.
Consequences: More ops discipline; lower risk.
Alternatives: Managed SaaS (loses data ownership — rejected).
Status: Accepted · Date: 2026-08-07
Context: We needed a complete, reproducible CRM data model for procurement (organizations, suppliers, factories, buyers, products/categories, RFQs, quotes, orders, logistics/QC providers, partners, documents, locations) on top of the live EspoCRM v10.
Decision:
RFQ-, QT-, PO- prefixes) via the built-in number field type.Supplier, RFQ, Order, legalName…) — we set customPrefixDisabled=true and use skipCustomPrefix in the installer, so API paths are clean (/api/v1/Supplier) and future UI-created entities match. Accepted risk: a future core/extension entity with the same name would conflict (mitigated below).scripts/espocrm/structure.json and applied by a scripted installer (apply_structure.php) that drives EspoCRM's own EntityManager/FieldManager/LinkManager tools — identical to the admin UI but idempotent, versioned, and re-runnable.Product/Category/Quote entities overlap with ours — decision then: keep ours (procurement-specific, preferred) or migrate data and disable ours; do not install Sales Pack blindly on top.C-prefixed names (safe but ugly API paths, inconsistent future UI entities — rejected); (b) manual Entity Manager clicks (not reproducible — rejected); (c) separate entities without Account master (data duplication, no dedupe point — rejected).