Every option it reads

Nine things: six attributes, two globals, one function. Nothing else is looked at.

b.js reads exactly nine things: six attributes on the script tag, two globals and one function. Nothing else is looked at.

Read the “stored” line first

An option can be set perfectly and still have its value discarded on the server, because storing a declaration is a policy decision the operator makes and storing a measurement is not. Each option below says which switch decides that. The panel’s Settings › Beacon card shows the same reference with that answered from your configuration, which is the only place it can be answered concretely.

01 · The nine options
data-endpoint attribute always — the script uses it itself

The collector URL, when it is not a sibling of b.js.

data-endpoint="https://loghound.example.com/collect.php"
Default
the script’s own src with b.js swapped for collect.php
Accepted
Any URL. Set it only for a CDN or a different path
data-hb attribute always

Heartbeat interval in milliseconds. A beat is sent only when engaged time actually advanced, so an idle tab produces one, not hundreds.

data-hb="30000"
Default
15000
Accepted
Integer, clamped to 2 000–300 000. Anything else is ignored and the default used
data-idle attribute always

How long after a real interaction a visitor still counts as engaged. This is the definition of the Engaged clock.

data-idle="60000"
Default
30000
Accepted
Integer, clamped to 1 000–600 000
data-ident attribute beacon.store_identity — false by default, so this is discarded until you turn it on

An identity your site attaches. Never guessed. See identity.

data-ident="<?= htmlspecialchars($user->email, ENT_QUOTES) ?>"
Default
absent, and absent is not empty
Accepted
Free text, truncated to 128 bytes. Control characters stripped, invalid UTF-8 repaired
data-signed-in attribute beacon.store_signed_in — true by default

Whether the visitor was signed in. Splits every number in the panel into signed-in and anonymous.

data-signed-in="<?= $user->isSignedIn() ? '1' : '0' ?>"
Default
absent — meaning not reported, never “no”. An attribute that is present but empty is an answer, and the answer is no
Accepted
1/0 or true/false. An empty attribute — the usual shape of a template that renders nothing for a visitor who is not signed in — is read as false. Any other value is read as not reported
data-params attribute beacon.query_params — empty by default, and the server’s list is authoritative

Query parameter names whose values are kept as search terms. Nothing else in the query string is read.

data-params="q,category,sort"
Default
absent — no parameter is collected
Accepted
Comma separated. At most 8 names, each up to 40 characters of a-z 0-9 _ - . [ ]. Each value capped at 96 characters, dropped rather than truncated if longer
window.LoghoundIdent global beacon.store_identity

The same value as data-ident, for a template where adding an attribute to the tag is awkward but setting a variable above it is not.

<script>window.LoghoundIdent = "ada@example.com";</script>
Default
unset
Accepted
A string, set before b.js executes — with defer, anywhere in the document. The attribute wins if both are present
window.LoghoundSignedIn global beacon.store_signed_in

The same value as data-signed-in.

<script>window.LoghoundSignedIn = true;</script>
Default
unset — not reported
Accepted
A real boolean, or the same strings the attribute accepts. Set before b.js executes
window.loghound.identify(ident, signedIn) function both switches above

Attach either value after the page has loaded — a single-page application that signs somebody in without a navigation, which no attribute can express.

window.loghound.identify(user.email, true);
Default
never called
Accepted
Both arguments optional and independent. Makes no request of its own: the values ride the heartbeat that is already scheduled. Safe to call with anything; it cannot throw into your code

data-hb and data-idle mirror beacon.heartbeat_ms and beacon.idle_timeout_ms in the configuration, and the settings page renders the snippet with your configured values already in it. The rest have no configuration counterpart on the page — your template supplies them per request — but whether Loghound stores what they carry is decided entirely on the server.

02 · A tag using several of them
<script src="https://loghound.example.com/b.js?v=1"
        data-hb="15000"
        data-idle="30000"
        data-signed-in="1"
        data-params="q"
        defer></script>

And the server-side half that decides what is kept, in config/loghound.php:

'beacon' => [
    'heartbeat_ms'    => 15000,
    'idle_timeout_ms' => 30000,
    'store_identity'  => false,
    'store_signed_in' => true,
    'query_params'    => ['q'],
    'allowed_hosts'   => [],
],

allowed_hosts is the one key there that answers to no tag attribute at all. It names the hostnames whose beacons may create a session, have their hostname recorded as a virtual host, and have their search terms kept. Empty is the right default for a machine that only measures sites whose access logs it already reads. Put the snippet on a site this installation has no log for and you have to add that hostname here, or the beacon is accepted, answered 204, and quietly kept out of every number — which looks exactly like a broken tag. The standalone page has the whole rule, including how a listed host is told apart from anybody else claiming to be it.

03 · What has to be in the page before b.js runs

The six data- attributes are read off the script tag itself, so where they sit in the document cannot be wrong. The two globals can be: window.LoghoundIdent and window.LoghoundSignedIn are read once, at the moment b.js executes, so a script that sets them after that has set them for nothing — the beacon has already sent its first payload and neither value is in it. With defer on the tag, b.js runs only after the document is parsed, which means anywhere in the page is early enough.

<!-- right: the globals are set before b.js executes -->
<script>
  window.LoghoundIdent = "<?= htmlspecialchars($user->email, ENT_QUOTES) ?>";
  window.LoghoundSignedIn = <?= $user->isSignedIn() ? 'true' : 'false' ?>;
</script>
<script src="https://loghound.example.com/b.js?v=1" defer></script>

For a value that genuinely is not known until later — a single-page application that signs somebody in without a navigation — the globals are the wrong instrument and window.loghound.identify(ident, signedIn) is the right one. It may be called at any point after b.js has run, and it sends nothing by itself: the values ride the next heartbeat.

window.loghound.identify(user.email, true);
04 · Why this list is a contract

The option list, with each default, accepted range and the key that decides whether what it sends is stored, is a contract in the Loghound specification: it belongs in the documentation, in the settings card, and in the script, and the three must not be able to drift. A test in the repository derives the list from b.js mechanically and fails when either of the other two falls behind.

The interface is the primary place, not the documents

Somebody installing a beacon reads the settings card. An option that exists only in a JavaScript comment and a markdown file is undocumented for the person who needs it — and the card answers what no document can: whether this installation will keep what an option sends.

Loghound is open source and MIT licensed. Questions about the Opensolr half — the account, the indexes, the plan — go to opensolr.com/contact; questions about the software itself belong on GitHub.

Loghound Documentation