The seed mechanism loads a realistic pharmaceutical supply chain scenario into a running EPCIS 2.0 app instance. It is designed for local development and manual exploration — not for test suites (tests create their own data).
make docker-up # start the full stack (DB + app)
make seed # load seed data (safe to run multiple times)To return to the seeded state after fiddling:
make db-reset # truncate all tables + clear graph, then re-seedA pharmaceutical manufacturer produces bottles of medication from a bulk raw material lot, packages them into cases and a pallet, ships to a distribution centre, which then unpacks and forwards to a pharmacy. A second smaller batch from the same lot is later recalled before delivery.
BULK LOT (L001)
│
├─[TransformationEvent: manufacturing]─► Bottle B001
│ Bottle B002
│ Bottle B003
│ Bottle B004
│
│ ┌────────────────────────────────────────────────────────┐
│ │ PACKAGING │
│ │ B001 + B002 ──[pack]──► Case C001 ──┐ │
│ │ B003 + B004 ──[pack]──► Case C002 ──┴──[load]──► Pallet P001
│ └────────────────────────────────────────────────────────┘
│ │
│ [ship: plant → DC]
│ │
│ DC receives P001
│ │
│ ┌─────────────┴──────────────┐
│ │ DC UNPACKING │
│ │ P001 ──[unpack]──► C001, C002
│ │ C001 ──[unpack]──► B001, B002
│ │ C002 ──[unpack]──► B003, B004
│ └────────────────────────────┘
│ │
│ [ship: DC → pharmacy]
│ │
│ Pharmacy receives B001–B004
│
└─[TransformationEvent: mfg recall]──► Bottle B005
│
[load]──► Pallet P002
│
[dispatch] ← VOIDED (recall)
Ten locations are seeded across two facility hierarchies.
| URI | Name | Parent |
|---|---|---|
urn:epc:id:sgln:0614141.plant00.0 |
Manufacturing Plant | — |
urn:epc:id:sgln:0614141.plant00.line1 |
Production Line 1 | Plant |
urn:epc:id:sgln:0614141.plant00.qlab0 |
Quality Lab | Plant |
urn:epc:id:sgln:0614141.plant00.fgsto |
Finished Goods Store | Plant |
urn:epc:id:sgln:0614141.plant00.ship0 |
Outbound Dock | Plant |
| URI | Name | Parent |
|---|---|---|
urn:epc:id:sgln:0614141.dc0001.0 |
Distribution Centre | — |
urn:epc:id:sgln:0614141.dc0001.recv0 |
DC Inbound Dock | DC |
urn:epc:id:sgln:0614141.dc0001.stor0 |
DC Storage | DC |
urn:epc:id:sgln:0614141.dc0001.ship0 |
DC Outbound Dock | DC |
| URI | Name | Parent |
|---|---|---|
urn:epc:id:sgln:0614141.rx0001.0 |
City Pharmacy | — |
The WD (within-descendant) filter flattens these hierarchies. A query with WD_bizLocation=urn:epc:id:sgln:0614141.plant00.0 matches all five plant locations.
| EPC | Description |
|---|---|
urn:epc:id:sgtin:0614141.100001.L001 |
Bulk raw material lot |
urn:epc:id:sgtin:0614141.200001.B001 |
Manufactured bottle 1 |
urn:epc:id:sgtin:0614141.200001.B002 |
Manufactured bottle 2 |
urn:epc:id:sgtin:0614141.200001.B003 |
Manufactured bottle 3 |
urn:epc:id:sgtin:0614141.200001.B004 |
Manufactured bottle 4 |
urn:epc:id:sgtin:0614141.200001.B005 |
Recall batch bottle |
| EPC | Description | Contents |
|---|---|---|
urn:epc:id:sgtin:0614141.300001.C001 |
Case 1 | B001, B002 |
urn:epc:id:sgtin:0614141.300001.C002 |
Case 2 | B003, B004 |
urn:epc:id:sscc:0614141.0000000001 |
Pallet 1 | C001, C002 |
urn:epc:id:sscc:0614141.0000000002 |
Pallet 2 (recall) | B005 |
All 17 events have fixed UUIDs (urn:uuid:00000000-0000-0000-0000-00000000000N) to ensure idempotency.
| # | UUID suffix | Type | bizStep | Location | Key EPCs |
|---|---|---|---|---|---|
| 1 | …0001 |
ObjectEvent (ADD) | receiving |
Production Line 1 | bulk lot L001 |
| 2 | …0002 |
TransformationEvent | manufacturing |
Production Line 1 | L001 → B001–B004 |
| 3 | …0003 |
ObjectEvent (OBSERVE) | inspecting |
Quality Lab | B001–B004 |
Event 1 carries two bizTransactionList entries: a purchase order (btt:po) and a bill of lading (btt:bol).
| # | UUID suffix | Type | bizStep | Location | Key EPCs |
|---|---|---|---|---|---|
| 4 | …0004 |
AggregationEvent (ADD) | packing |
Finished Goods Store | B001+B002 → C001 |
| 5 | …0005 |
AggregationEvent (ADD) | packing |
Finished Goods Store | B003+B004 → C002 |
| 6 | …0006 |
AggregationEvent (ADD) | loading |
Finished Goods Store | C001+C002 → P001 |
| # | UUID suffix | Type | bizStep | Location | Key EPCs |
|---|---|---|---|---|---|
| 7 | …0007 |
TransactionEvent (OBSERVE) | shipping |
Outbound Dock | P001 |
| 8 | …0008 |
ObjectEvent (OBSERVE) | receiving |
DC Inbound Dock | P001 |
Event 7 carries three bizTransactionList entries (PO, BOL, invoice) and sourceList / destinationList with owning-party GLNs.
| # | UUID suffix | Type | bizStep | Location | Key EPCs |
|---|---|---|---|---|---|
| 9 | …0009 |
AggregationEvent (DELETE) | unpacking |
DC Storage | P001 → C001, C002 |
| 10 | …0010 |
AggregationEvent (DELETE) | unpacking |
DC Storage | C001 → B001, B002 |
| 11 | …0011 |
AggregationEvent (DELETE) | unpacking |
DC Storage | C002 → B003, B004 |
DELETE-action aggregation events represent disassembly. They generate CONTAINS edge removals in the AGE graph.
| # | UUID suffix | Type | bizStep | Location | Key EPCs |
|---|---|---|---|---|---|
| 12 | …0012 |
TransactionEvent (OBSERVE) | shipping |
DC Outbound Dock | B001–B004 |
| 13 | …0013 |
ObjectEvent (ADD) | receiving |
Pharmacy | B001–B004 |
| # | UUID suffix | Type | bizStep | Location | Key EPCs |
|---|---|---|---|---|---|
| 14 | …0014 |
TransformationEvent | manufacturing |
Production Line 1 | L001 → B005 |
| 15 | …0015 |
AggregationEvent (ADD) | loading |
Outbound Dock | B005 → P002 |
| 16 | …0016 |
ObjectEvent (OBSERVE) | shipping |
Outbound Dock | P002 |
| 17 | …0017 |
VoidedEvent | — | — | voids event 16 |
Event 16 is captured then immediately voided by event 17. It appears in query results with a voidedEvent field referencing event 17.
The seed data exercises every traceability path the API supports.
GET /trace/urn:epc:id:sgtin:0614141.200001.B001
Returns the full event history for B001, walking backwards through: received at pharmacy ← shipped from DC ← (was in case C001) ← packed ← QC'd ← manufactured ← bulk lot received.
GET /trace/urn:epc:id:sgtin:0614141.100001.L001
Shows both transformation events (batch 1 producing B001–B004, recall batch producing B005) — demonstrating that a single input EPC can fan out to multiple outputs.
GET /hierarchy/urn:epc:id:sscc:0614141.0000000001
Returns the tree: Pallet P001 → [Case C001 → [B001, B002], Case C002 → [B003, B004]].
GET /hierarchy/urn:epc:id:sgtin:0614141.300001.C001
Returns: Case C001 → [B001, B002].
GET /masterdata/urn:epcglobal:epcis:vtype:BusinessLocation
Returns all 10 seeded locations. Each entry includes the parentUri where applicable.
GET /masterdata/urn:epcglobal:epcis:vtype:BusinessLocation?EQ_ATTR_country=US
Matches the plant, DC, and pharmacy (all tagged "country": "US").
GET /events?EQ_bizLocation=urn:epc:id:sgln:0614141.plant00.0
Returns only events whose bizLocation is the plant root (events 7 and 8 in the scenario). Does not include events at sub-locations.
GET /events?WD_bizLocation=urn:epc:id:sgln:0614141.plant00.0
Returns all 10 plant-side events (phases 1, 2, and the recall sub-chain), including those at production line, QC lab, finished goods store, and outbound dock.
GET /events?WD_bizLocation=urn:epc:id:sgln:0614141.dc0001.0
Returns all 5 DC-side events (phases 3 and 4: receipt, unpacking).
GET /events?EQ_bizStep=urn:epcglobal:cbv:bizstep:shipping
Returns events 7, 12, and 16 (the three shipping events).
GET /events?EQ_bizStep=urn:epcglobal:cbv:bizstep:packing&EQ_bizStep=urn:epcglobal:cbv:bizstep:loading
Returns all 4 aggregation events that pack/load items (phases 2 and recall).
GET /events?EQ_epcList=urn:epc:id:sscc:0614141.0000000001
Returns all events mentioning pallet P001 directly (events 6 [load], 7 [ship], 8 [recv at DC], 9 [unpack]).
GET /events?EQ_parentID=urn:epc:id:sscc:0614141.0000000001
Returns the two aggregation events where P001 is the parent (events 6 and 9).
GET /events?EQ_bizTransaction_po=urn:example:po:PO-2026-002
Returns event 7 (plant→DC shipment carrying that PO).
GET /events?EQ_bizTransaction_inv=urn:example:inv:INV-2026-043
Returns event 12 (DC→pharmacy shipment).
GET /events?eventType=TransformationEvent
Returns events 2 and 14 — the two manufacturing transformations from the same bulk lot.
GET /events?action=DELETE
Returns events 9, 10, and 11 — the three DC unpacking events.
GET /events/urn:uuid:00000000-0000-0000-0000-000000000016
Returns event 16 (the recall dispatch) with a voidedEvent block referencing event 17.
Every event uses a fixed eventID UUID. The capture handlers return 409 Conflict when a UUID already exists; make seed treats 409 as a no-op and prints:
~ ObjectEvent bulk lot received (already exists)
Masterdata upserts (POST /masterdata/locations) use ON CONFLICT DO UPDATE and are always safe to re-run.
First run (empty DB):
+ urn:epc:id:sgln:0614141.plant00.0
+ urn:epc:id:sgln:0614141.plant00.line1
...
+ ObjectEvent bulk lot received
+ TransformationEvent bulk → 4 bottles
...
Subsequent run (seeded DB):
~ urn:epc:id:sgln:0614141.plant00.0 (updated)
...
~ ObjectEvent bulk lot received (already exists)
...
make db-reset wipes the database and graph, then calls make seed:
Truncating relational tables and clearing graph...
→ TRUNCATE epcis_raw_events, capture_jobs, masterdata_locations, subscriptions
→ MATCH (n) DETACH DELETE n (AGE graph)
Re-seeding...
App healthy — seeding http://localhost:3000
...
The truncation runs through scripts/truncate.sql via docker-compose exec -T db psql. It issues a single TRUNCATE … CASCADE for the relational tables followed by the AGE Cypher command in the same psql session (required because LOAD 'age' and SET search_path must apply before the Cypher call).
Never use
docker-compose down -vto reset — that destroys the volume and requires running migrations again. Usemake db-resetinstead.
The seed binary lives in src/bin/seed.rs and is built as a separate Cargo binary. It uses the same reqwest client that ships as a library dependency and requires no extra crate features.
It connects to http://localhost:3000 by default. To target a different address, edit the API constant at the top of the file or add an env-var override.
The binary does not bypass the API — all data flows through the capture and masterdata endpoints, so every graph edge (ENCOUNTERED, CONTAINS) is created by the same code paths that handle production traffic. This means the seed data is representative: trace, hierarchy, and WD filter queries all work exactly as they would in production.