Skip to main content
Version: 0.3.0 (dev)

Enable lookup enrichment

Optional path after Install Padas Motion and a running Motion Engine. Lookup is not required for the sample TCP pipeline.

PDL lookup reads a local replica on the engine. It does not call lookup-server once per event. Keep [lookup].enabled off until lookup-server is enrolled, started, and this engine is cache-bound.

Use Install Padas Motion for UI + engine (same rpm/deb/tgz pattern). Enroll/start pitfalls (TTL, code reuse, --ca-file): padas-motion operator runbook Phases 3–6.

1. Install the nested lookup package​

From $PADAS_HOME (usually /opt/padas), install packages/padas-lookup-* with the same format as the engine. Do not use tar --strip-components=1 (root must stay lookup/).

test -x "$PADAS_HOME/lookup/bin/padas-lookup-server"

2. Enroll lookup​

Same flags as the engine: --ui-url and --code. There is no --service flag.

In the console, open Settings → Services (/services?service=lookup) and choose the Lookup tab. Create an inventory row (name + lookup-server HTTPS base URL, default port 8998). The console registers a matching S2S client (client_id = inventory id). Use Enroll code (or Rotate & enroll code), then on the lookup host:

"$PADAS_HOME/lookup/bin/padas-lookup-server" enroll \
--ui-url https://<host>:9000 \
--code <enrollment-uuid>

Automation without the SPA (admin session cookie) remains available via the embedded AS admin API; prefer Settings → Services (Lookup tab) for day-to-day operators.

Optional --ca-file PATH when the console TLS cert is not trusted by the default store (same enroll pitfalls as the engine: padas-motion operator runbook).

Enroll does not bind REST (8998) or gRPC (50099).

3. Start lookup-server​

Same systemd vs foreground pattern as the engine; unit name is padas-lookup.service.

sudo -u padas "$PADAS_HOME/lookup/bin/padas-lookup-server"
curl --insecure https://127.0.0.1:8998/ready

Expect HTTP 200 when the store is open and gRPC is bound.

Optional: create a Lookup reader client​

For Prometheus or read-only automation, use Settings → Services (Lookup tab) → Add reader client or Settings → Services (Service clients tab) → Add client → Lookup. The console creates a grant-ready client limited to aud=padas-lookup and lookup:read; copy its secret once. Copy example produces Prometheus configuration for the Lookup inventory targets. Do not reuse the Lookup enroll client, which also carries write scope.

4. Enable the engine consumer and restart​

Lookup stays disabled in shipped defaults. Add connectivity only to $PADAS_HOME/core/etc/padas.toml. Do not list table names in TOML.

[lookup]
enabled = true
http_url = "https://127.0.0.1:8998"
grpc_addr = "https://127.0.0.1:50099"
cache_path = "./data/lookup-cache"
binding_poll_interval_secs = 60

[lookup.tls]
verify_cert = false
# ca_file = "etc/certs/install-ca.crt" # when verify_cert = true

Same-host HTTPS defaults match the commented block in core/etc/padas.default.toml. If lookup-server is on another host, point http_url / grpc_addr at that host.

Shipped [[core.auth.outbound]] already includes audience padas-lookup and scope lookup:read. Do not duplicate it.

Restart the engine after this overlay. Enabling [lookup] without a restart leaves PDL lookup a silent no-op.

sudo systemctl restart padas-core.service

Tarball / foreground: stop and run "$PADAS_HOME/core/bin/padas" start again.

Lab overlays that use http://127.0.0.1:8998 are for TLS-off test harnesses. Packaged lookup-server listens HTTPS by default — do not copy lab http:// URLs into this install.

Keys: Motion Engine TOML → Lookup.

5. Configure population and tables​

Motion 0.2.0 populates tables from the console with Upload CSV only (Lookup connector authoring and deploy are a later slice).

Open Management → Lookup, choose the enrolled service, and use Upload CSV. Create or upload geo_ip with ip in Index fields (and optionally Row id field). Optional Max matches, Match order, Delimiter, and First row is header map to the upload API; leave them empty to keep defaults (1, stable, comma, header on). Then use View / Edit / Download / Delete on the table row. Console View and Download are the first 1000 rows; a larger table is truncated. Full CSV is lookup-server GET /api/v1/lookup/tables/{table}/export (lookup:read), not those buttons. To change index columns, max matches, or match order on an existing table, PATCH that table; do not re-upload. Query matches one indexed field and one value.

The browser calls the authenticated UI BFF; it never calls port 8998 directly. This is admin ingest, not the PDL hot path.

Automation can still call lookup-server connector REST directly; the console does not author or deploy those connectors in 0.2.0.

Automation alternative: call lookup-server REST directly

Mint a token from the console AS using the enrolled Lookup client (lookup:write). client_id matches the inventory row id from Settings → Services (Lookup tab). The secret is on the Lookup host:

export PADAS_HOME=/opt/padas
CLIENT_ID=<lookup-client-id>
SECRET=$(sudo cat "$PADAS_HOME/lookup/data/security/${CLIENT_ID}.client.secret")

TOKEN=$(curl -sk -X POST "https://127.0.0.1:9000/api/v1/auth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "client_id=${CLIENT_ID}" \
--data-urlencode "client_secret=${SECRET}" \
--data-urlencode "scope=lookup:write" \
--data-urlencode "aud=padas-lookup" \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))")

Create geo_ip indexed on ip (JSON body). Repeat with /upload and -F file=@geo_ip.csv for a CSV file (index_fields / row_id_field as query params).

curl -sk -X POST "https://127.0.0.1:8998/api/v1/lookup/tables/geo_ip?index_fields=ip&row_id_field=ip" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '[{"key":"10.0.0.1","ip":"10.0.0.1","city":"Amsterdam","country":"NL"}]'

Confirm metadata (row_count, index_fields) with GET /api/v1/lookup/tables/geo_ip. Change indexes without replacing rows (lookup:write):

curl -sk -X PATCH "https://127.0.0.1:8998/api/v1/lookup/tables/geo_ip" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"index_fields":["ip","city"]}'

Full CSV is GET /api/v1/lookup/tables/geo_ip/export with lookup:read. Do not drive the pipeline by fetching rows over REST on every event.

6. Cache-bind the engine instance_uuid​

Table lists live in cache bindings, not in Core TOML. After enroll, the first successful core status probe writes service_instance_uuid on the Core inventory row. Open Management → Cores, choose Pick tables on the Core row, select geo_ip, and save. The UI resolves the Core's Lookup endpoint and uses that UUID as the wire consumer_id; operators do not paste UUIDs.

An empty table list still yields a silent PDL no-op. Binding poll defaults to 60 seconds—wait one interval (or restart the engine) before expecting replica rows.

Advanced/debug: bind by UUID with direct REST

The consumer id is [service].instance_uuid if set, otherwise [core].instance_uuid in $PADAS_HOME/core/etc/padas.toml:

grep instance_uuid "$PADAS_HOME/core/etc/padas.toml"
CID=<that-uuid>

If both [service].instance_uuid and [core].instance_uuid are present, bind [service].instance_uuid. The engine uses that value when it is set; otherwise it falls back to [core].instance_uuid.

Register then assign (PUT without a prior GET can 404):

curl -sk "https://127.0.0.1:8998/api/v1/lookup/cache-bindings/${CID}" \
-H "Authorization: Bearer $TOKEN"

curl -sk -X PUT "https://127.0.0.1:8998/api/v1/lookup/cache-bindings/${CID}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"tables":["geo_ip"]}'

7. Pipeline PDL​

If the event has src_ip and the table indexes ip, name the index column first and the event field after AS:

lookup geo_ip ip AS src_ip OUTPUT country, city

Add that as the PDL Query on a processing task (Tasks), put the task in a pipeline, then assign and deploy (Management → Pipelines).

OUTPUT copies those columns onto the event. lookup() inside eval is for a single value; it returns null when the store is missing — same no-op class as the command.

Troubleshooting​

SymptomLikely causeFix
Pipeline runs; extra fields never appear[lookup].enabled still false or overlay not loadedSet enabled = true in core/etc/padas.toml; restart the engine
Same, after enableEmpty cache binding (tables: []) or wrong consumer_idBind the UUID from padas.toml; PUT {"tables":["geo_ip"]}
Fields appear then freeze / stay emptylookup-server down; replica stale or never syncedcurl --insecure https://127.0.0.1:8998/ready; start padas-lookup-server; wait for poll
Index missEvent field name differs from the index column and the command has no ASlookup geo_ip ip AS src_ip OUTPUT country, city (§7)
Silent no-opNo store injected (enabled false, empty URLs, or manager not started)Command leaves the event unchanged; lookup() is null — not a PDL parse error
LOOKUP_TABLE_NOT_FOUND on PUT bindingTable not created yetCreate/upload the table first (§5)
Token 401Used engine lookup:read client for writeMint with the lookup enroll client and scope=lookup:write

Enrichment missing after a good PUT: wait binding_poll_interval_secs, then re-check task output. Do not add per-event GET …/rows to the pipeline.

Where next​