> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

# Fingerprint to BigQuery

This server flow turns raw request signals into a privacy-friendly identifier before events reach BigQuery. The Express source extracts the client IP and user agent, the fingerprint transformer hashes them into `user.hash`, and the GCP destination writes the events to a table. The ingest keys must be `ip` and `userAgent`, the fingerprint's default inputs (`ingest.ip`, `ingest.userAgent`); the transformer anonymizes the IP, reduces the user agent and keys the hash with a secret salt.

```json
{
  "version": 4,
  "flows": {
    "default": {
      "config": {
        "platform": "server",
        "bundle": {
          "packages": {
            "@walkeros/collector": { "version": "^4" },
            "@walkeros/server-source-express": { "version": "^4" },
            "@walkeros/server-transformer-fingerprint": { "version": "^4" },
            "@walkeros/server-destination-gcp": { "version": "^4" }
          }
        }
      },
      "sources": {
        "express": {
          "package": "@walkeros/server-source-express",
          "next": "fingerprint",
          "config": {
            "ingest": {
              "map": {
                "ip": { "key": "ip" },
                "userAgent": { "key": "headers.user-agent" }
              }
            }
          }
        }
      },
      "transformers": {
        "fingerprint": {
          "package": "@walkeros/server-transformer-fingerprint",
          "config": {
            "settings": { "salt": "$env.FINGERPRINT_SALT", "length": 16 }
          }
        }
      },
      "destinations": {
        "bigquery": {
          "package": "@walkeros/server-destination-gcp",
          "import": "destinationBigQuery",
          "config": {
            "settings": {
              "projectId": "my-analytics-project",
              "datasetId": "events",
              "tableId": "raw_events"
            }
          }
        }
      },
      "collector": { "run": true }
    }
  }
}
```

## Run it[​](#run-it "Direct link to Run it")

Save the config as `flow.json` and run the fingerprint transformer of the built flow on an event. `--ingest` passes the request context the Express source extracts, the client IP and user agent, and the salt comes from the environment. Nothing is written to BigQuery:

```bash
FINGERPRINT_SALT=change-me npx walkeros push flow.json -e '{"name":"page view"}' --simulate transformer.fingerprint \
  --ingest '{"ip":"203.0.113.7","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"}'
```

You should see, besides an `Output:` line naming the built bundle before each `Simulating` line (your hash differs, as it depends on the salt and the day):

```bash
Simulating transformer: fingerprint
success: true
  transformer.fingerprint
    event {"name":"page view","user":{"hash":"8015a6ca3b754e5e"}}
  Duration: 58621ms
```

## What you get[​](#what-you-get "Direct link to What you get")

In production every event lands as one row in your table, using the standard walkerOS schema (name, entity, action, timestamp, user, and more). The destination receives an `appendRows` call, and the `user` column carries the hash in place of a raw IP or user agent (from the package's examples):

```bash
['appendRows', [{
  "name": "page view",
  "entity": "page",
  "action": "view",
  "user": "{\"hash\":\"158f99cc06e33fd6\"}",
  ...
}]]
```

The hash rotates daily by default, so a visitor is not tracked across days. Use a long random salt per deployment in production.

## Next steps[​](#next-steps "Direct link to Next steps")

* [**Fingerprint transformer**](/preview/pr-749/docs/transformers/fingerprint.md): Salt, rotation, site separation, and extra fields
* [**GCP destination**](/preview/pr-749/docs/destinations/server/gcp.md): BigQuery table setup and mapping
