Is LibKey Nomad safe?

Low risk

LibKey Nomad is low risk. LibKey Nomad generates a permanent device ID on first run. Landing on a page with a DOI, PubMed ID, or ISBN sends that ID plus your device fingerprint to a.thirdiron.com/events, linking every scholarly page you visit to one device.

Third Iron LLCv1.60.0Chrome Web Store
22Risk
Who publishes it

Third Iron LLC - no other listings under this identity, 15 shared hostnames

What this publisher told the store about itself, and the other listings that told it the same thing.

Shared hosts - 15 hostnames

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

a.thirdiron.com
Also called by 1 other listing: LibKey Nomad
analytics.thirdiron.com
Also called by 1 other listing: LibKey Nomad
auth.thirdiron.com
Also called by 1 other listing: LibKey Nomad
nomad-api-cdn.thirdiron.com
Also called by 1 other listing
staging-a.thirdiron.com
Also called by 1 other listing
staging-auth.thirdiron.com
Also called by 1 other listing: LibKey Nomad
staging-nomad-api-cdn.thirdiron.com
Also called by 1 other listing
api.thirdiron.com
Also called by 2 other listings
assets.thirdiron.com
Also called by 2 other listings
nomad-api.thirdiron.com
Also called by 2 other listings
staging-analytics.thirdiron.com
Also called by 2 other listings
staging-api.thirdiron.com
Also called by 2 other listings
support.thirdiron.com
Also called by 2 other listings
browzine.com
Also called by 3 other listings
api.mendeley.com
Also called by 5 other listings, including Direct Paper Importer, Mendeley Referencer, Get Full Text Research (GetFTR)

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Persistent Device ID Fingerprints You on Every Research Page

MediumunwantedCWE-359Dynamic sandbox

LibKey Nomad generates a permanent device ID on first run.

What actually happens

You did this

You visit a page containing a scholarly-article identifier, a DOI, PubMed ID, or ISBN, on any website, not just academic ones.

The extension's content script runs on every http(s) and file:// page, so this can fire anywhere a citation identifier appears.

The extension did this

The extension reads a permanent per-device ID from local storage, creating one on first use, and sends it with your full browser/OS fingerprint to Third Iron's analytics server.

The same device ID is reused on every future event, letting the server link your scholarly browsing across sessions and sites.

Fields included in every analytics event sent to a.thirdiron.com/events

  • Permanent device ID
    7f492ad3-074c-4668-b4b6-3ef745e13040

    A UUID generated once and stored forever, letting Third Iron link every event from this browser together indefinitely.

  • Full browser user-agent string
    Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36

    Identifies your exact browser build and OS in one string, narrowing you down within the broader population of visitors.

  • Operating system
    macOS 10.15.7

    Your OS name and version, sent as separate fields alongside the user-agent string.

  • Browser and version
    Chrome 146.0.0.0

    Your browser name and exact version number.

  • Device make and model
    Apple Macintosh

    The hardware vendor and model reported by your browser.

  • Your institution
    158 (Arizona State University Library)

    The library you selected during onboarding, adding organizational context on top of the persistent device ID.

Captured request

POSThttps://a.thirdiron.com/events
Body
{  "events": [    {      "eventCategory": "NomadArticleLinkPresented",      "eventType": "PubMed",      "os": "macOS",      "osVersion": "10.15.7",      "deviceMake": "Apple",      "deviceModel": "Macintosh",      "browser": "Chrome",      "browserVersion": "146.0.0.0",      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",      "uniqueDeviceId": "7f492ad3-074c-4668-b4b6-3ef745e13040",      "libraryId": 158,      "url": "not captured"    }  ],  "token": "QnVvJNPw2LNnqx8b3AF5ecm0ULInX2"}

The code that does this

Reading/creating the permanent device ID and attaching it to every analytics event

Readable version

Deobfuscated: getBackgroundDimensions() / getAnalyticsDimensions()

static/background.js
async getBackgroundDimensions() {  let e = new U(navigator.userAgent).getResult(),    t = await this.getStoredUniqueDeviceId();  return t || (t = i(), await this.setStoredUniqueDeviceId(t)), {    appName: `Nomad`,    appVersion: browser.runtime.getManifest().version,    property: bt.PRODUCTION_FOREGROUND,    os: e.os.name,    osVersion: e.os.version,    uniqueDeviceId: t,    deviceMake: e.device.vendor,    deviceModel: e.device.model,    browser: e.browser.name,    browserVersion: e.browser.version,    userAgent: e.ua,    url: `not captured`  }}async getAnalyticsDimensions(e, t, n = null) {  return {    messageType: `analytics`,    eventCategory: e,    eventType: t,    extras: {      installType: await this.getStorageData(`installType`),      ...await this.getBackgroundDimensions(),      ...n    }  }}

Deobfuscated: the analytics client that POSTs the batch with a shared token

static/background.js
yt = function() {  function e(e) {    this.mDelegate = e, this.mFailedEventQueue = new vt(e)  }  return e.prototype.getFailedEventCount = function() {    return this.mFailedEventQueue.getFailedEventCount()  }, e.prototype.sendEvent = function(e) {    var t = _t.newEventOutput(this.mDelegate, e);    return this.sendEventOutput(t, t.property)  }, e.prototype.sendEventOutput = function(e, t) {    var n = this.mFailedEventQueue.checkOutFailedEventsQueue();    n.push(e);    var r = this.createFinalPayload(n);    return this.sendFinalPayload(r, t)  }, e.prototype.sendFinalPayload = function(e, t) {    var n = this;    return new Promise(function(t, r) {      var i = JSON.stringify(e);      n.mDelegate.postEvent(i, {        onSuccess: function() {          return t()        },        onFailure: function(t) {          return n.onEventSendFailed(t, e), r(t)        }      })    })  }, e.prototype.onEventSendFailed = function(e, t) {    this.mDelegate.logError(e.actualError), this.mFailedEventQueue.onEventSendFailed(t.events, e)  }, e.prototype.createFinalPayload = function(e) {    return {      events: e,      token: this.mDelegate.getClientToken()    }  }, e}(),bt;(function(e) {  e.PRODUCTION_FOREGROUND = `production_foreground`, e.NON_PRODUCTION_FOREGROUND = `non_production_foreground`})(bt ||= {});var xt = function() {    function e() {}    return e.getServerUrl = function(t) {      switch (t) {        case bt.PRODUCTION_FOREGROUND:          return e.PRODUCTION_SERVER_URL;        case bt.NON_PRODUCTION_FOREGROUND:          return e.STAGING_SERVER_URL;        default:          return e.STAGING_SERVER_URL      }    }, e.PRODUCTION_SERVER_URL = `https://analytics.thirdiron.com/events`, e.STAGING_SERVER_URL = `https://staging-analytics.thirdiron.com/events`, e.CLIENT_TOKEN = `QnVvJNPw2LNnqx8b3AF5ecm0ULInX2`, e.MAX_OFFLINE_QUEUED_EVENTS = 256, e.DEFAULT_REQUEST_TIMEOUT_MS = 3e4, e  }(),

Where the fingerprinted analytics events go

    • a.thirdiron.com

    Production analytics endpoint for Third Iron LLC, LibKey Nomad's developer. Receives the persistent device ID and full browser/OS/device fingerprint on every qualifying page.

    • staging-a.thirdiron.com

    Staging counterpart of the same analytics endpoint, used when the extension's internal 'useStaging' flag is set.

What it can do

Permissions this extension asks for, as declared in version 1.60.0. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on every site you visit

    <all_urls>

  • Store data in your browser

    storage

  • Schedule its own background tasks

    alarms

  • See every page you navigate to, as you navigate to it

    webNavigation

Updated 2 November 2026lkoeejijapdihgbegpljiehpnlkadljb