== compose restart policy (vendor file) ==
15:# `restart:` key on any service (Docker default "no"). The postgres service and
== container RestartPolicy ==
no maxretry=0
== container cmd/entrypoint ==
entrypoint=[goatcounter] cmd=[serve -automigrate] user=goatcounter:goatcounter workdir=/home/goatcounter
== help serve: store-every / ratelimit / db ==
33:  -db          Database connection: "sqlite+<file>" or "postgres+<connect>"
34-               See "goatcounter help db" for detailed documentation. Default:
35-               sqlite+./db/goatcounter.sqlite3 if that database file exists, or
36-               sqlite+./goatcounter-data/db.sqlite3 if it doesn't.
--
115:  -ratelimit   Set rate limits for various actions; the syntax is
116-               "name:num-requests/seconds"; multiple values are separated by
117-               a comma. The defaults are:
118-
--
133:  -store-every How often to persist pageviews to the database, in seconds.
134-               Higher values will give better performance, but it will take a
135-               bit longer for pageviews to show. The default is 10 seconds.
136-
== tables ==
name api_tokens bots browser_stats browsers campaign_stats campaigns exports hit_counts hit_stats hits iso_3166_1 language_stats languages location_stats locations paths ref_counts refs sites size_stats sqlite_sequence store system_stats systems users version  
== hits columns ==
name,type hit_id,INTEGER site_id,INTEGER path_id,INTEGER ref_id,INTEGER session,BLOB first_visit,INTEGER browser_id,INTEGER system_id,INTEGER campaign,INTEGER width,smallint location,varchar language,varchar created_at,timestamp  
== bots columns ==
name,type site_id,INTEGER path,TEXT bot,INTEGER user_agent,TEXT created_at,timestamp  
== sites ==
site_id,cname,settings
1,bench.example,"{""public"":""private"",""secret"":"""",""allow_counter"":false,""allow_bosmang"":false,""data_retention"":0,""ignore_ips"":"""",""collect"":190,""collect_regions"":""US,RU,CN"",""allow_embed"":""""}"

== sqlite journal_mode ==
journal_mode
wal

== served count.js: how the URL is built ==
1:// GoatCounter: https://www.goatcounter.com
2:// This file is released under the ISC license: https://opensource.org/licenses/ISC
12:		catch (err) { console.error('invalid JSON in data-goatcounter-settings: ' + err) }
23:		var data = {
24:			p: (vars.path     === undefined ? goatcounter.path     : vars.path),
25:			r: (vars.referrer === undefined ? goatcounter.referrer : vars.referrer),
26:			t: (vars.title    === undefined ? goatcounter.title    : vars.title),
27:			e: !!(vars.event || goatcounter.event),
28:			s: window.screen.width,
29:			b: is_bot(),
30:			q: location.search,
79:			console.warn('goatcounter: ' + msg)
129:		data.rnd = Math.random().toString(36).substr(2, 5)  // Browsers don't always listen to Cache-Control.
135:		return endpoint + urlencode(data)
147:		if (!navigator.sendBeacon(url)) {
151:			img.src = url
182:					event:    true,
184:					title:    (elem.dataset.goatcounterTitle || elem.title || (elem.innerHTML || '').substr(0, 200) || ''),
185:					referrer: (elem.dataset.goatcounterReferrer || elem.dataset.goatcounterReferral || ''),
207:			opt.attr   = opt.attr   || {width: '200', height: (opt.no_branding ? '60' : '80')}
== probes (http_code X-Goatcounter) ==
a. plain GET, no XFF (client IP = socket peer as the container sees it): 200 
b. RATE LIMIT TRAP: 8 back-to-back GETs, no XFF, same UA (default -ratelimit count:4/1 keyed on IP+UA):
   1: 200 
   2: 200 
   3: 200 
   4: 200 
   5: 429 
   6: 429 
   7: 429 
   8: 429 
c. PRIVATE XFF IS DISCARDED: 8 back-to-back GETs with X-Forwarded-For: 10.50.0.<i> (RealIP() skips private/TEST-NET values -> same bucket as b):
   1: 200 
   2: 200 
   3: 200 
   4: 200 
   5: 200 
   6: 200 
   7: 200 
   8: 429 
d. PUBLIC XFF: 8 back-to-back GETs with X-Forwarded-For: 24.0.8.<i> (distinct buckets -> all 200):
   1: 200 
   2: 200 
   3: 200 
   4: 200 
   5: 200 
   6: 200 
   7: 200 
   8: 200 
e. bot UA (Googlebot): 200  (200 expected; row goes to bots, not hits)
f. Host: other.example (no such site; single-site fallback serves the only site): 200
g. prefetch (Purpose: prefetch): 200  (200 expected, NOT stored)
h. missing p= : HTTP/1.1 400 Bad Request X-Goatcounter: not valid: path: must be set, must be longer than 1 characters. 
i. b=153 (client-side webdriver flag): 200 (200 expected; -> bots)
j. b=5 (invalid): 400 (400 expected)
k. POST /count (sendBeacon path): 200
l. response body of a 200: 43 bytes, content-type image/gif
== after probes (+14 s > store-every 10 s) ==
hits 0 -> 0 (delta 0; expect 200-count minus prefetch/bots: a1 + b/c/d 200s + f1 + k1 + l2)
bots 1 -> 3 (delta 2; expect 2: Googlebot UA + b=153)
distinct sessions 0 -> 0
== STORAGE MODEL (vendor default) ==
sites.settings.collect=190 (256 = CollectHits 'Individual pageviews' is NOT set by default since v2.6.0)
hit_counts visits for the site = 36  (aggregates count first_visit per session+path since v2.4.0 — probe b's 4 pageviews from one IP on one path = 1 visit)
per path:
   path,visits
   /id/1,1
   /id/10,1
   /id/11,1
   /id/12,1
   /id/13,1
   /id/14,1
   /id/15,1
   /id/16,1
   /id/17,1
   /id/18,1
   /id/19,1
   /id/2,1
   /id/20,1
   /id/3,1
   /id/4,1
   /id/5,1
   /id/6,1
   /id/7,1
   /id/8,1
   /id/9,1
   /probe/a,1
   /probe/b,1
   /probe/c,1
   /probe/d,8
   /probe/e,1
   /probe/f,1
   /probe/k,1
   /probe/l,1
   /probe/l2,1
   
