Skip to content

Aurora multi-site demo, local run

This runs Aurora’s multi-plant routing story end to end on one machine: an order whose lines live at different sites is searched across all sites, routed to the fewest sites, split into one order per site, and sent. Every write goes to a sandbox connection that sends nothing anywhere.

It uses Aurora’s own item and customer master (Products Table.xlsx, sent 2026-10-19). That file is covered by the NDA. Keep it and the sample you cut from it outside the repo, and never commit either.

  • Nine active connections, one per Aurora site, all of type mock-epms. Ohio is the default. The type matters: demo has no adapter, so a send to it fails with “Unsupported ERP type”. mock-epms accepts the write and does nothing.
  • Per connection: about 120 items, customers and one ship-to per customer, cut from the master.
  • Aliases for customers the master links across sites by Salesforce id but names differently. These are a ranking boost in customer search. They do not make a customer appear when the search text does not match, so a customer with a different name at another site still needs a manual pick.
  • One fixture order, “Lake Cable multi-plant PO”. Its three lines are carried at two sites, and the customer exists at Ohio and at Lunenburg under different ids.

The site-to-town mapping and which two sites run IQMS are our reading of the nine Site IDs in the master and Robert’s “seven Great Plains, two IQMS”. Treat both as unconfirmed.

  • Docker, for Postgres and Typesense. The scripts refuse to run against anything that is not localhost.
  • The Temporal CLI, for a local dev server.
  • Access to the staging secrets in Infisical, used only so you can sign in through the staging Clerk. The database, Typesense, Temporal and Redis settings below override the staging ones, so no data leaves your machine.
  • The multi-site review features from the stacked PRs 4332 to 4338 (line facility saved on a cross-site pick, Find fewest locations, Group by location, the customer-at-facility check, per-split customer and ship-to). Without them the seed still loads, but the review page behaves as it does on staging today.
  1. Start the services.

    Terminal window
    docker run -d --name aurora-pg -e POSTGRES_PASSWORD=postgres -p 55433:5432 postgres:16-alpine
    docker run -d --name aurora-ts -e TYPESENSE_API_KEY=local-dev-key -e TYPESENSE_DATA_DIR=/data \
    -v aurora-ts-data:/data -p 8108:8108 typesense/typesense:29.1
    temporal server start-dev --port 7233 --ui-port 8233
  2. Create the schema. Use db:push, not db:migrate: the migration history assumes a schema that already exists.

    Terminal window
    docker exec aurora-pg psql -U postgres -c "CREATE DATABASE aurora_ms;"
    docker exec aurora-pg psql -U postgres -d aurora_ms -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
    cd packages/db
    DATABASE_URL=postgresql://postgres:postgres@localhost:55433/aurora_ms npx drizzle-kit push --force
  3. Cut the sample from the master. The output stays outside the repo.

    Terminal window
    uv run --with openpyxl python scripts/poc-aurora/multi-site/build_sample.py \
    "Products Table.xlsx" /path/outside/repo/aurora_master_sample.json
  4. Start the TypeScript worker against the local database only.

    Terminal window
    cd apps/ts-temporal-worker
    env DATABASE_URL=postgresql://postgres:postgres@localhost:55433/aurora_ms USE_PG_DRIVER=1 \
    TEMPORAL_ADDRESS=localhost:7233 TEMPORAL_NAMESPACE=default pnpm dev

    Billing usage logs a MISSING_CONFIG failure without INTERNAL_SERVICE_TOKEN. The quote workflow still completes.

  5. Start the webapp with staging auth and local everything else, then sign in with your own account and note the org you land in.

    Terminal window
    cd apps/webapp
    infisical run --env=staging --path=/runtime/webapp --projectId=<project id> -- \
    env DATABASE_URL=postgresql://postgres:postgres@localhost:55433/aurora_ms USE_PG_DRIVER=1 \
    TYPESENSE_HOST=localhost TYPESENSE_PORT=8108 TYPESENSE_PROTOCOL=http \
    TYPESENSE_API_KEY=local-dev-key TYPESENSE_SEARCH_API_KEY=local-dev-key \
    TYPESENSE_ADMIN_API_KEY=local-dev-key TYPESENSE_EXPORT_API_KEY=local-dev-key \
    TEMPORAL_ADDRESS=localhost:7233 TEMPORAL_NAMESPACE=default TEMPORAL_TLS_CERT= TEMPORAL_TLS_KEY= \
    TEMPORAL_TASK_QUEUE=erp-unlocked-ts REDIS_URL= \
    FLAG_OVERRIDES=erp_multi_connection_v1,erp_connection_routing_rules_v1,review_approval_only_mode=false \
    pnpm dev --port 4321

    The project id and the reasons for each override are in .agents/skills/webapp-local-db-staging-auth/SKILL.md. FLAG_OVERRIDES is a development-only switch. It turns multi-site on and approval-only off for this process. Approval-only blocks submit and quote generation before the split checks run, so it has to be off to send.

  6. Seed, using the org id you noted.

    Terminal window
    env DATABASE_URL=postgresql://postgres:postgres@localhost:55433/aurora_ms ORG_ID=org_xxx \
    TYPESENSE_URL=http://localhost:8108 TYPESENSE_API_KEY=local-dev-key \
    AURORA_MASTER_SAMPLE=/path/outside/repo/aurora_master_sample.json \
    uv run --project packages/pdf-shared python scripts/poc-aurora/multi-site/seed.py

    It prints the review page path. Re-running refuses if the org already has connections. Set RESET=1 to delete the org’s previous demo rows first.

Open the review page and go to the Items tab.

  1. Find fewest locations on the routing card checks every site for each line and proposes the smallest set of sites that covers the order. Nothing binds until you click Apply. Here it proposes three lines from two sites.
  2. Or search by hand. Search all locations in the item search shows each result with its site code, and picking an item from another site asks you to confirm.
  3. On the Split tab, Group by location merges lines at the same site into one order per site.
  4. Each job routed away from Ohio shows Customer at Lunenburg. It fills in when exactly one customer there matches the order’s customer name, then the ship-to when the customer has exactly one. Otherwise you search and pick.
  5. Send creates one order per site. The Lunenburg order carries Lunenburg’s customer and ship-to ids, and the Ohio order carries Ohio’s.

If a job’s customer is not on file at its site, the send is refused with a message naming the site.

  • There is no PDF behind the fixture, so the document pane says “Document preview unavailable”. Putting a file in R2 would write to a shared bucket, so this recipe does not.
  • The sandbox adapter returns no ERP order number, so the submitted jobs show none.
  • POST /api/notifications/workflow returns 400 when a quote has no error to report, so no notification is created. This does not affect the send.
  • Nothing here validates Aurora’s real ERPs. Great Plains and IQMS have no connector.

Seeding staging is a separate decision, and seed.py refuses to run against it. When that decision is made, the org’s feature flags change in Flagsmith: add erp_multi_connection_v1 to the Aurora org’s segment overrides and remove review_approval_only_mode. The seed would also need to write its Typesense documents to the shared cluster, scoped by connection_id like every other customer, and the demo cleanup script would need to learn about the nine connections.