Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,365 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

OpenLinker

CI Version License: Apache 2.0 Status: alpha

openlinker.io · Architecture · Integrations · vs BaseLinker

Sync orders, inventory, and listings between your shop and the marketplaces you sell on. Self-hosted, open-source, pluggable.

If you sell on your own shop and on marketplaces like Allegro, you've already lived the pain: orders coming in from multiple places, inventory drifting between channels, manually rewriting product descriptions for every listing, customer data scattered across systems. The usual answers are SaaS channel managers (you pay forever and your data lives on their servers) or custom scripts (fragile and yours to maintain alone). OpenLinker is the third option: a self-hosted, open-source orchestration platform that does the same job, on your servers, with your data, with code you can read and extend.

Status: alpha, pre-1.0. Expect rough edges; report them.

OpenLinker admin dashboard — jobs, connections, and recent orders at a glance


What you can do today

  • Orders flow automatically. Every Allegro order creates a PrestaShop order, with the buyer auto-provisioned as a customer. Cursor-based and resumable — nothing's lost if anything pauses.
  • Stock stays in sync, both ways. Sell on Allegro, your shop stock drops. Restock in your shop, Allegro listings update. No per-platform spreadsheet.
  • Create listings without copy-pasting. Pick a product, pick a category (or look it up by EAN), get an Allegro offer up with images, parameters, GPSR data, and your saved seller policies — from one wizard. AI can draft the description if you let it.
  • Run multiple stores from one dashboard. Each connection has its own encrypted credentials and config. Two PrestaShop stores plus three Allegro accounts? One OpenLinker instance.
  • You own everything. Self-hosted, Apache 2.0. No subscription, no per-order pricing, no vendor that can shut you off. You host it; that's the trade.

Connections page — multi-store, multi-marketplace from one instance   Create-offer wizard — category, parameters, GPSR, policies in one flow


Integrations

Integration Role Status
PrestaShop Shop (source + destination) ✅ Live
WooCommerce (guide) Shop (source + destination + inventory) ✅ Live
Allegro (guide) Marketplace (source + offers + shipping) ✅ Live
Erli (guide) Marketplace (offers + source) ✅ Live
AI router (Anthropic, OpenAI) Content suggestion ✅ Live
InPost (guide) Shipping (ShipX — paczkomat + kurier, labels, webhooks) ✅ Live
DPD (guide) Shipping (REST labels + protocols, SOAP tracking) ✅ Live
Subiekt nexo (guide · tutorial) Invoicing (via Sfera bridge — first InvoicingPort adapter) ✅ Live
KSeF (guide · tutorial) Invoicing (Polish national e-invoicing) ✅ Live
Infakt Invoicing (accounting SaaS — issues invoices and relays them to KSeF on the seller's behalf) ✅ Live
Shopify · BigCommerce · Magento Shop 📋 Planned
eBay · Amazon · OLX · Empik · Bol Marketplace 📋 Planned
DHL · FedEx · ORLEN Paczka · GLS Shipping (siblings under ShippingProviderManagerPort) 📋 Planned
Fakturownia · iFirma · wFirma Invoicing (siblings under InvoicingPort) 📋 Planned

Planned items are open for community contributions — see Adding your own integration below.


Capabilities

OpenLinker is built around a small set of capability ports. Each integration implements one or more of them — adding a new platform means adding implementations, not changing core code.

Capability What it does Integrations
Catalog & inventory Read products, variants, and stock from a master shop PrestaShop · WooCommerce
Orders Ingest from any source (event journal or watermark); create + manage them in a destination shop PrestaShop (source + destination) · WooCommerce (source + destination) · Allegro (source) · Erli (source)
Offers / listings Manage marketplace offers — categories, prices, quantities, seller policies, GPSR data Allegro · Erli
Shipping Generate labels + handover protocols, fetch tracking (via ShippingProviderManagerPort) Allegro · InPost · DPD
Invoicing Issue fiscal documents and, where supported, submit them to a tax authority for clearance (via InvoicingPort) Subiekt nexo · KSeF · Infakt
Content suggestion Provider-agnostic AI completions with editable, versioned prompts AI router (Anthropic, OpenAI)
Auth & ops Per-integration connection testers, webhook provisioners, OAuth, retry classifiers, credentials/config validators All integrations

See docs/capabilities.md for the full, code-synced vocabulary — every capability port, its base contract, and all 31 sub-capabilities with descriptions and guards — and docs/architecture-overview.md for the contracts plugin authors implement against.

Host-provided, regardless of integration: encrypted credentials store · multi-connection per platform type (two PrestaShop stores from one instance) · identifier mapping with a single unified seed (ol_product_*, ol_order_*, ol_variant_*, …) · customer identity resolution with optional email-fallback · PII-aware storage (full or hash-only) · sync-job orchestration with retry classification + outcome tracking · browser-based admin UI.


Implementations

PrestaShop · WebService v1

Role: shop / destination

OpenLinker's PrestaShop integration. Implements the full shop surface: catalog and inventory reads, order ingestion via the date_upd watermark, order creation with auto-provisioned guest customers, status lifecycle, cancellations, and returns. Ships the OL Dynamic Carrier PrestaShop module so buyer-paid shipping costs from marketplaces round-trip correctly into PrestaShop orders without breaking existing carriers. Registers connection tester, webhook provisioner, and connection-config + credentials shape validators with the host.

~7.6k LOC source · libs/integrations/prestashop/ · README · also the recommended starting point when adding a new shop or marketplace plugin

Allegro · Public API

Role: marketplace / source

OpenLinker's Allegro integration. Implements every offer sub-capability the OfferManager port defines — OfferLister, OfferEventReader, OfferFieldUpdater, CategoryBrowser, CategoryBarcodeMatcher, CategoryParametersReader, CatalogProductReader, OfferCreator, OfferStatusReader, OfferReader, SellerPoliciesReader, ResponsibleProducerReader, SafetyAttachmentUploader. Order ingestion via the Allegro event journal with cursor persistence. Full OAuth flow with shared token state, refresh-on-401 retry, and a plugin-owned migration. Email normalization for Allegro's masked-buyer-email format.

~7.5k LOC source · libs/integrations/allegro/ · also a useful template for any marketplace plugin that needs OAuth

AI · Anthropic, OpenAI

Role: content suggestion

OpenLinker's AI router for content generation. Wraps Anthropic and OpenAI behind a single AiCompletionPort, with per-provider encrypted key storage, admin-switchable active provider, and Anthropic prompt-caching wired in. Drives the offer-description suggestion flow in the create-offer wizard.

~572 LOC source · libs/integrations/ai/ · also a useful template for plugins that route through an external SaaS rather than per-connection


Why OpenLinker?

  • vs SaaS channel managers (BaseLinker, ChannelEngine, ChannelAdvisor) — you own the data, you own the code, and your bill is your hosting bill. No per-order pricing, no vendor lock-in, no surprise terms changes.
  • vs custom scripts — a tested foundation that doesn't break the next time Allegro changes their API. Real integration tests run against a real PrestaShop install via Testcontainers.
  • vs headless commerce platforms (Medusa, Vendure, Spree) — OpenLinker is the integration plumbing for the shop you already have, not a replacement shop platform. Keep PrestaShop (or WooCommerce, BigCommerce, …) running; add OpenLinker alongside it.

What we cover (and what we don't)

How OpenLinker measures against the standard set of multichannel-orchestration flows. Honest about gaps — they're either on the roadmap or intentionally out of scope.

Legend: ✅ supported today · ⚠️ partial · 🛣️ on the roadmap · — out of current scope

Flow Status Capability / port
Orders
Ingest orders from any source (marketplace or shop) OrderSourcePort
Create + manage orders in destination shop OrderProcessorManagerPort
Generate invoices / fiscal documents (+ tax-authority clearance) InvoicingPort — Subiekt nexo, KSeF, Infakt
Accounting export / GL / valuation Out of scope; integrate accounting separately
Inventory & catalog
Read products, variants, attributes ProductMasterPort
Read inventory levels InventoryMasterPort
Multi-location / multi-warehouse stock 🛣️ InventoryMasterPort accepts locationId; not yet exercised by an adapter
Bulk offer creation Bulk offer-creation batch (Allegro) + bulk shop-publish (WooCommerce)
Bulk quantity batch-update ⚠️ OfferQuantityBatchUpdater sub-capability defined; no adapter implements it yet
Marketplace listings
Create + update offers (categories, policies, GPSR) OfferManagerPort + sub-capability ports
AI offer descriptions AiCompletionPort
Repricing / dynamic pricing rules 🛣️ PricingAuthorityPort planned
Customers
Identity resolution across channels Customer-identity service + plugin-provided email normalizer
GDPR-friendly PII modes (full or hash-only) Host-provided storage policy
Customer Q&A / messaging Out of scope
Shipping & carriers
Carrier mapping (marketplace ↔ physical carrier) Host-provided carrier-mapping service
Pickup-point handling (Paczkomat, etc.) Through OrderSourcePort order shape
Buyer-paid shipping cost round-trip OrderProcessorManagerPort + plugin-owned shop module
Generate shipping labels + handover protocols ShippingProviderManagerPort — InPost, DPD, Allegro
Fetch tracking + push status back to marketplace ShippingProviderManagerPort tracking + order-status writeback (ADR-027)
Payments
Payment status from order data Part of the IncomingOrder shape from OrderSourcePort
Refunds / returns lifecycle 🛣️ OL-owned order-status state machine deferred (#1032); the refunded payment status is read-only forward-compat (no source emits it in v1)
Payment provider integrations (Stripe, P24, …) 🛣️ PaymentProcessorPort planned
Operations
Multiple shops + marketplaces in one instance Host-provided connection model
Multi-user with roles ⚠️ Host-provided JWT + admin role gate; finer-grained roles are a known gap
Encrypted credentials store Host-provided crypto service
Webhooks (HMAC + replay + dedup) Host-provided webhook pipeline
Sync-job retry + outcome tracking Host-provided sync-job service + plugin-provided retry classifier
Workflow automation ("when X, do Y" rules) Out of scope; write a worker handler instead
Reporting / analytics dashboard Out of scope; export via the API

Spot a gap that's on your evaluation checklist? Open a feature request — the table is a living scorecard, not a fixed roadmap.


How it works

Marketplace (Allegro)                           Shop (PrestaShop)
   │                                                   ▲
   │ 1. order event                                    │ 4. create order,
   ▼                                                   │    update status
[OrderSourcePort]                          [OrderProcessorManagerPort]
   │                                                   ▲
   │ 2. hydrate                                        │ 3. map customer,
   │    full order                                     │    resolve products
   ▼                                                   │
              OpenLinker core ─────────────────────────┘
              (identifier mapping · retries · dedup · projections)

Every platform — shop or marketplace — implements the same set of typed capability ports (OrderSourcePort, OrderProcessorManagerPort, ProductMasterPort, InventoryMasterPort, OfferManagerPort). Core orchestration is platform-agnostic; per-platform behaviour lives in self-registering plugin packages. Full picture in Architecture Overview.


For developers

OpenLinker is built so that adding a platform is a plugin, not a fork.

Adding your own integration

A new integration is a workspace package under libs/integrations/<name>/. The path from idea to merged PR:

  1. Scaffold. pnpm create-adapter <your-platform> generates the package skeleton — package.json, tsconfig, a stub plugin descriptor, and the canonical hexagonal layout.
  2. Pick your roles. Shop, marketplace, content suggestion, or something new entirely? That decides which capability ports you'll implement. Implement only the ports your platform supports; capability guards handle the rest at runtime.
  3. Implement. Each port has 1–10 methods. PrestaShop is the multi-port reference (~7.6k LOC across catalog, inventory, orders). Allegro is the OAuth + marketplace reference (~7.5k LOC, every offer sub-capability). The AI router is the thin single-port example (572 LOC). Effort scales with how much of a platform you cover.
  4. Register. Add your plugin to apps/api/src/plugins.ts (and apps/web/src/plugins/index.ts for any FE contributions — routes, nav, wizards, error renderers).
  5. Test. Use @openlinker/test-kit for integration tests against real Postgres + Redis + a real shop install via Testcontainers. Use the in-memory fakes from each context's /testing subpath for unit tests.

Full walkthrough in the Plugin Author Guide. The PrestaShop reference adapter README is the closest thing to a worked example.

Why it's pleasant to extend

  • Capabilities are open-world. Capability, EntityType, and PlatformType are open strings at the registry boundary. Add ShippingProvider, PricingAuthority, or anything else without a core PR — the well-known set is a hint, not a gate.

  • Framework-free domain. libs/core/src/**/domain/ has zero NestJS / TypeORM imports. Runtime-enforced: @openlinker/core exposes only 28 explicit subpaths via package.json#exports, ESLint blocks deep imports in plugin packages, and package exports block the runtime path entirely.

  • Framework-neutral plugin SDK. @openlinker/plugin-sdk ships the AdapterPlugin contract, the HostServices bag, and the typed dispatchCapability<T> helper. Plugins compile against the SDK, not against the host's NestJS / TypeORM.

  • Frontend is pluggable too. Routes, nav items, the typed ApiClient, offer-creation wizards, structured error renderers — all extension points populated from a per-platform plugin folder under apps/web/src/plugins/. Allegro and PrestaShop are the in-tree examples.

  • Real tests, exported. Integration tests run against real Postgres + Redis + a real PrestaShop install via Testcontainers. The harness ships as @openlinker/test-kit. Each context exports in-memory fakes from a /testing sub-barrel for plugin unit tests.

Wanted

Any 📋 Planned row in the Integrations table is open for community contribution — or propose something not listed via the new_integration issue template. The Plugin Author Guide walks through port selection, package layout, registry wiring, OAuth, and tests.

Where we're at

OpenLinker is moving fast and publicly. See recent activity for what's landed lately, the modularity audit (#546) for the architectural thread making the codebase plugin-ready, and the OSS launch epic (#670) for what's blocking the first public release.


One-command demo

Want to click through OpenLinker without setting up a dev environment? The demo overlay boots the entire stack in Docker — PostgreSQL, Redis, MySQL, a seeded PrestaShop, and the full OpenLinker app tier (API + Worker + admin UI) — from a clean checkout:

For a full step-by-step walkthrough (example .env, PrestaShop + Allegro connection wiring, end-to-end verification, and troubleshooting) see the One-Command Demo Setup Guide.

git clone https://github.com/openlinker-project/openlinker.git
cd openlinker

# Required pre-step: provide the credentials encryption key (not committable).
# .env.example already ships an empty OPENLINKER_CREDENTIALS_ENCRYPTION_KEY=
# line, so drop it and append the generated one instead of `>> .env`, which
# would leave two lines with the same key (harmless — env-file parsing is
# last-wins — but confusing to anyone editing .env by hand).
grep -v '^OPENLINKER_CREDENTIALS_ENCRYPTION_KEY=' .env.example > .env
echo "OPENLINKER_CREDENTIALS_ENCRYPTION_KEY=$(openssl rand -base64 32)" >> .env

pnpm demo:up        # builds the images and starts everything (first run takes a while)
pnpm demo:logs      # follow the logs
pnpm demo:down      # stop the stack (add -v to also wipe the data volumes)

Required: OPENLINKER_CREDENTIALS_ENCRYPTION_KEY must be set (via the root .env) before pnpm demo:up. The credentials-encryption migration fails closed without it and aborts the boot. Generate one with openssl rand -base64 32. See .env.example.

Database migrations run automatically (one-shot migrate container) before the API and Worker start — no manual migration:run needed.

Service URL Credentials
OpenLinker admin UI http://localhost:8090 admin / admin
OpenLinker API http://localhost:3000
PrestaShop storefront http://localhost:8080
PrestaShop admin http://localhost:8080/admin-dev demo@prestashop.com / prestashop_demo

phpMyAdmin (PrestaShop MySQL browser, http://localhost:8081, root / root) is a devtools-only auxiliary service — it's gated behind a Compose devtools profile rather than always-on. Opt in with pnpm dev:stack:devtools:up (plain dev stack) or pnpm demo:devtools:up (demo overlay).

PrestaShop auto-installs with a seeded catalog and the OpenLinker module pre-mounted. The PrestaShop ↔ OpenLinker connection itself is configured manually in the admin UI (Connections → New) — see the Operator Guide.

Configuring the PrestaShop connection (container networking): the API and Worker containers reach PrestaShop over the Compose network by its service name, not localhost. When creating the connection, set both the Shop URL and the Storefront URL to http://prestashop (not http://localhost:8080) — localhost from inside a container resolves to the container itself. The demo pre-configures PrestaShop for this (a prestashop-domain shop URL plus disabled canonical redirect) via a post-install step, so webservice calls and server-side product-image downloads (OpenLinker fetches image bytes itself, then re-uploads them to the marketplace CDN) resolve correctly.

Known limitation (follow-up): with the Storefront URL set to http://prestashop, product-image download works from the app tier, but the operator's browser can't resolve prestashop, so product thumbnails in the OpenLinker UI won't render. Decoupling the server-side download base from the browser display base is a tracked follow-up — for the demo, prefer a working image sync over rendered thumbnails.

Notes:

  • ⚠️ Shared volumes: the demo shares the same Compose project (openlinker) and data volumes as pnpm dev:stack:up. On a machine with an existing local dev stack, running the demo reuses and can clobber that data — the two flows are not isolated. Stop and, if needed, wipe one before running the other (pnpm dev:stack:down / pnpm demo:down -v). Requires Docker Compose ≥ 2.24.
  • VITE_API_BASE_URL is baked into the UI bundle at image build time (default http://localhost:3000); rebuild the web image to change it.
  • Demo credentials are intentionally not production-safe — this stack is for local evaluation only.

Quickstart

git clone https://github.com/openlinker-project/openlinker.git
cd openlinker
pnpm install
cp apps/api/.env.example apps/api/.env.local
cp apps/worker/.env.example apps/worker/.env.local
# apps/web/.env.example ships safe defaults; copy only if you need to override VITE_API_BASE_URL

pnpm dev:stack:up                                    # PostgreSQL · Redis · MySQL · PrestaShop · WooCommerce in Docker
pnpm --filter @openlinker/api migration:run          # Create database tables
pnpm start:dev:api                                   # NestJS API on :3000
pnpm start:dev:worker                                # Background job worker
pnpm start:dev:web                                   # React admin UI on :4173

Then follow docs/getting-started.md — a walkthrough from a clean machine to a working PrestaShop + Allegro setup with categories mapped.

Prerequisites

  • Node.js 22+ (LTS), pnpm 10+
  • Docker + Docker Compose (dev stack + integration tests)
  • An Allegro sandbox account if you want to exercise the marketplace path

Runtime requirements

The exact versions the Docker images and Compose files are built/tested against:

Component Version
Node.js 22 (pinned node:22.23.1-alpine3.24 in Dockerfile / apps/web/Dockerfile)
pnpm 10 (pinned pnpm@10.33.4, matching package.json's packageManager field)
Docker Compose ≥ 2.24 (required by the demo overlay's !reset/!override merge keys)
PostgreSQL 17 (postgres:17-alpine)
Redis 8.4 (redis:8.4-alpine)
MySQL 8.4.7 (mysql:8.4.7-oraclelinux9)

The dev stack starts PostgreSQL, Redis, MySQL, PrestaShop, and WooCommerce in containers — you do not need any of those installed locally.

⚠️ Upgrading an existing checkout (one-time, #1411).

Two changes combine into a one-time data reset of your local dev/demo stack:

  1. Every named volume was renamed _data-data (e.g. postgres_datapostgres-data). Docker qualifies volumes as <project>_<name>, so your existing openlinker_postgres_data is orphaned and Compose creates an empty openlinker-postgres-data.
  2. PostgreSQL moved 16 → 17. PG17 refuses to start against a PG16 data directory (database files are incompatible with server), so preserving the old volume by hand would not work either.

The next pnpm dev:stack:up / pnpm demo:up therefore starts with an empty database, silently — your configured connections, encrypted credentials, seeded PrestaShop catalog, and WooCommerce setup are still on disk in the orphaned volumes, just invisible. Re-seed from scratch and reclaim the disk:

docker compose down
docker volume rm \
  openlinker_postgres_data openlinker_redis_data openlinker_mysql_data \
  openlinker_prestashop_data openlinker_woocommerce_mysql_data \
  openlinker_woocommerce_data openlinker_caddy_data openlinker_caddy_config
pnpm dev:stack:up   # re-seeds from scratch

Adjust the openlinker_ prefix if you set COMPOSE_PROJECT_NAME. This affects local dev/demo data only — there is no production deployment to migrate.

WooCommerce is available at http://localhost:8082 (PrestaShop uses 8080). Run pnpm dev:stack:wc-credentials after startup to retrieve the auto-generated consumer key and secret. See the WooCommerce Setup Guide for full configuration steps.


Development

pnpm lint              # ESLint
pnpm type-check        # TypeScript strict mode, all workspaces
pnpm test              # Unit tests (no Docker needed)
pnpm test:integration  # Integration tests via Testcontainers (needs Docker)
pnpm format            # Prettier
pnpm build             # All workspaces

Quality gate before every commit: pnpm lint && pnpm type-check && pnpm test.

For schema changes, also run pnpm --filter @openlinker/api migration:show to confirm there are no missing migrations. See docs/migrations.md.


Architecture & docs


Contributing

Pull requests welcome. See CONTRIBUTING.md for the workflow, branch naming, and the Closes #N PR convention.

If you're adding a new platform, jump to Adding your own integration above for the five-step path.


Community

  • Code of Conduct — the standards we hold ourselves and contributors to
  • Support — where to ask questions vs. file bugs vs. propose features
  • Security — responsible-disclosure process (do not open public issues for vulnerabilities)

License

Apache License 2.0. See LICENSE.

About

Self-hosted, API-first e-commerce orchestration platform — sync products, inventory, and orders across any marketplace.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages